Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-125571

YAnimator onRunningChanged can Qt.callLater(jsFunc) with invalid scope object

    XMLWordPrintable

Details

    • Windows
    • a6700d247 (dev), 297d96a4f (6.8), af61e2f31 (6.7), 3771cedce (tqtc/lts-6.5)

    Description

      I have a type (lets call it MyType.qml) which looks something like:

      import QtQuick
      
      Rectangle {
          id: bgRect
      
          clip: true
          color: "lightsteelblue"
          width: 70
          height: 400
      
          property bool animationEnabled: true
          property real nextYPos: 0.0
      
          onNextYPosChanged: if (visible) maybeAnimate()
          function maybeAnimate() {
              if (animationEnabled) {
                  if (!yAnim.running && fgRect.y != nextYPos) {
                      yAnim.from = fgRect.y
                      yAnim.to = nextYPos
                      yAnim.start()
                  }
              }
          }
      
          Timer {
              running: true
              repeat: true
              interval: 450
              onTriggered: bgRect.nextYPos = Math.random() * bgRect.height
          }
      
          Rectangle {
              id: fgRect
              color: "blue"
              width: bgRect.width
              height: bgRect.height
              z: 2
          }
      
          YAnimator {
              id: yAnim
              target: animTarget
              easing.type: Easing.InOutQuad
              duration: 200
              onRunningChanged: if (!running) Qt.callLater(maybeAnimate)
          }
      }
      
      

      If you construct a whole lot of these and destroy them after random times (e.g. between 0.5 and 10 seconds) you should eventually hit the crash.

      The backtrace is:

       1  QQmlPrivate::loadObjectProperty<0>                                                                qqml.cpp                       907  0x7fff17ec9c23 
      2  QQmlPrivate::AOTCompiledContext::loadScopeObjectPropertyLookup                                    qqml.cpp                       1660 0x7fff17ecf483 
      3  `QmlCacheGeneratedCode::_qt_qml_MyType_qml::<lambda_10>::operator()'::`2'::<lambda_1>::operator() MyType_qml.cpp                 1554 0x7ff7991f916c 
      4  QV4::Moth::VME::exec                                                                              qv4vme_moth.cpp                536  0x7fff17e9a79d 
      5  <lambda_ca494cc21659423f5644870c1a1f4b97>::operator()                                             qv4functionobject.cpp          544  0x7fff17e0be39 
      6  QV4::convertAndCall<<lambda_ca494cc21659423f5644870c1a1f4b97>>                                    qv4jscall_p.h                  137  0x7fff17e0bc7f 
      7  QV4::ArrowFunction::virtualCall                                                                   qv4functionobject.cpp          557  0x7fff17e0a2a8 
      8  QQmlDelayedCallQueue::DelayedFunctionCall::execute                                                qqmldelayedcallqueue.cpp       43   0x7fff17f022a4 
      9  QQmlDelayedCallQueue::qt_static_metacall                                                          moc_qqmldelayedcallqueue_p.cpp 111  0x7fff17f0036f 
      10 QMetaCallEvent::placeMetaCall                                                                     qobject.cpp                    656  0x7fff177d8114 
      11 QObject::event                                                                                    qobject.cpp                    1437 0x7fff177d6303 
      12 QCoreApplication::notify                                                                          qcoreapplication.cpp           1201 0x7fff1778b5c6 
      13 QCoreApplication::notifyInternal2                                                                 qcoreapplication.cpp           1121 0x7fff1778b6ff 
      14 QCoreApplicationPrivate::sendPostedEvents                                                         qcoreapplication.cpp           1901 0x7fff1778e826 
      15 QWindowsGuiEventDispatcher::sendPostedEvents                                                      qwindowsguieventdispatcher.cpp 44   0x7fff185619af 
      16 QEventDispatcherWin32::processEvents                                                              qeventdispatcher_win.cpp       471  0x7fff17912d70 
      17 QWindowsGuiEventDispatcher::processEvents                                                         qwindowsguieventdispatcher.cpp 37   0x7fff18561989 
      18 QEventLoop::exec                                                                                  qeventloop.cpp                 182  0x7fff17792f8f 
      19 QCoreApplication::exec                                                                            qcoreapplication.cpp           1442 0x7fff17788c8d 
      20 main                                                                                              main.cpp                       300  0x7ff7991165a5 
      21 __scrt_common_main_seh                                                                            exe_common.inl                 288  0x7ff7995b9824 
      22 BaseThreadInitThunk                                                                               KERNEL32                            0x7ffff6b7257d 
      23 RtlUserThreadStart                                                                                ntdll                               0x7ffff77caa48 

      The relevant bit of code from the AOT generated cpp is:

      // generate_LoadQmlContextPropertyLookup
      while (!aotContext->loadScopeObjectPropertyLookup(58, &r2_0)) { 

      I am not sure, but I believe what is happening is that the YAnimator finishes running just before the MyType instance is destroyed, so it posts the `Qt.callLater(maybeAnimate)` event to the event queue; the instance is then destroyed, and then the callLater is serviced, so maybeAnimate is invoked with an invalid/deleted scope object.

       

      There is a bit of a hack workaround: if I instead have:

      ...
      
      function maybeAnimate(scopeObject) {
          if (scopeObject == null) return;
          if (animationEnabled) { ... }
      }
      
      onNextYPosChanged: if (visible) maybeAnimate(bgRect)
      
      yAnim.onRunningChanged: if (!running) Qt.callLater(maybeAnimate, bgRect)
      
      ...

      Then it looks like either (1) the reference keeps the scope object alive long enough to avoid the crash, OR (2) the check catches that the reference has become null and so the crash is avoided.  If it's (1) then I do wonder when the reference will be cleaned up, or if this is just a perma leak.  I didn't dig that far.

       

      Tested on windows, Qt 6.6.1, msvc2022, without NO_CACHEGEN specified.

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            qtqmlteam Qt Qml Team User
            chrisadams Christopher Adams
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes