Unset the QQmlAnimationTimer pointer from unregistered jobs
Amends 4b125a5bfd
, which reset the pointer
in registered jobs when the timer was destroyed. However, if a job is
unregistered explicitly before the timer is destroyed, then the job's
m_timer pointer might still be a dangling pointer, resulting in a crash
when the job is reactivated later.
So clear the job's pointer to QQmlAnimationTimer whenever it is
unregistered from the timer. Since a running job can then have a nullptr
timer, only assert in the job's destructor if the timer is not nullptr.
Introduce a test case that reliably crashes without the fix. The test
is added to the QQuickAnimation test as it uses a Qt Quick UI.
Fixes: QTBUG-98248
Pick-to: 6.2 6.2.2 5.15
Change-Id: Ief991900c50aefd480d9c79e83324968102ca29c
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
parent
a98747ab6c
commit
9c732286ed
|
@ -243,6 +243,7 @@ void QQmlAnimationTimer::registerRunningAnimation(QAbstractAnimationJob *animati
|
|||
|
||||
void QQmlAnimationTimer::unregisterRunningAnimation(QAbstractAnimationJob *animation)
|
||||
{
|
||||
unsetJobTimer(animation);
|
||||
if (animation->userControlDisabled())
|
||||
return;
|
||||
|
||||
|
@ -307,9 +308,10 @@ QAbstractAnimationJob::~QAbstractAnimationJob()
|
|||
|
||||
Q_ASSERT(m_state == Stopped);
|
||||
if (oldState == Running) {
|
||||
Q_ASSERT(QQmlAnimationTimer::instance(false) == m_timer);
|
||||
if (m_timer)
|
||||
if (m_timer) {
|
||||
Q_ASSERT(QQmlAnimationTimer::instance(false) == m_timer);
|
||||
m_timer->unregisterAnimation(this);
|
||||
}
|
||||
}
|
||||
Q_ASSERT(!m_hasRegisteredTimer);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
//main.qml
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: 640
|
||||
height: 480
|
||||
visible: true
|
||||
property bool running : false
|
||||
|
||||
Rectangle {
|
||||
id: rect
|
||||
anchors.fill: parent
|
||||
color: "red"
|
||||
|
||||
Component.onCompleted: {
|
||||
anim.start()
|
||||
running = true
|
||||
}
|
||||
}
|
||||
|
||||
OpacityAnimator {
|
||||
id: anim
|
||||
|
||||
target: rect
|
||||
from: 1
|
||||
to: 0
|
||||
duration: 20000
|
||||
}
|
||||
}
|
|
@ -116,6 +116,7 @@ private slots:
|
|||
void fastFlickingBug();
|
||||
void opacityAnimationFromZero();
|
||||
void alwaysRunToEndInSequentialAnimationBug();
|
||||
void cleanupWhenRenderThreadStops();
|
||||
};
|
||||
|
||||
#define QTIMED_COMPARE(lhs, rhs) do { \
|
||||
|
@ -1998,6 +1999,19 @@ void tst_qquickanimations::alwaysRunToEndInSequentialAnimationBug()
|
|||
QCOMPARE(whiteRect->property("opacity").value<qreal>(),1.0);
|
||||
}
|
||||
|
||||
void tst_qquickanimations::cleanupWhenRenderThreadStops()
|
||||
{
|
||||
QQuickView view(QUrl::fromLocalFile("data/cleanupWhenRenderThreadStops.qml"));
|
||||
view.show();
|
||||
view.setPersistentGraphics(false);
|
||||
view.setPersistentSceneGraph(false);
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
||||
QTest::qWait(50);
|
||||
view.hide();
|
||||
view.show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_qquickanimations)
|
||||
|
||||
#include "tst_qquickanimations.moc"
|
||||
|
|
Loading…
Reference in New Issue