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

Timer leak in QWaylandWindow

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P1: Critical
    • 5.12.9, 5.15.0
    • 5.14.0 Beta2
    • QPA: Wayland
    • None
    • Linux/Wayland
    • fabaaec60a5fe477f7339c0d825d8b236a7e5330 (5.15) d18c29931b0bc889fff66bdbde89133544ba0529 (5.12)

    Description

      Our application is able to trigger a timer leak in QWaylandWindow, although it's unclear how exactly the timer leak manifests itself. What we can observe is that the number of timers created by QWaylandWindow (or QWaylandEglWindow if observed from gammaray) grows overtime. In our case, after leaving the application running overnight, the number of timers created by QWaylandWindow gets over 100k. At which point, the main thread becomes so overloaded with activating timers such that the application becomes very sluggish.

      We suspect the timer leak may have something to do with threaded OpenGL rendering in our application, which is done in separate QWindow's with createWindowContainer().

      What we've found is that in QWaylandWindow::handleUpdate(), despite attempting to kill the callback timer as shown in the code snippet below, the value of mFrameCallbackTimerId is not always -1. If the value of fcbId (mFrameCallbackTimerId) is printed out immediately below fetch-and-store, it is consistently found to be -1. However, printing it out in the lamda slot just before starting a new timer, mFrameCallbackTimerId is not always -1, which seems to me is when the timer leaks.

      qtwayland/src/client/qwaylandwindow.cpp
      void QWaylandWindow::handleUpdate()
      {
          // ...
          // Stop current frame timer if any, can't use killTimer directly, see comment above.
          int fcbId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1);
          if (fcbId != -1)
              QMetaObject::invokeMethod(this, [this, fcbId] { killTimer(fcbId); }, Qt::QueuedConnection);
      
          // Start a timer for handling the case when the compositor stops sending frame callbacks.
          QMetaObject::invokeMethod(this, [this] { // Again; can't do it directly
              if (mWaitingForFrameCallback)
                  mFrameCallbackTimerId = startTimer(100);
          }, Qt::QueuedConnection);
      }
      

      The attached patch (against Qt 5.12.5) seems to have fixed the timer leak, the total number of active timers in our application never exceeds a couple hundreds after a two day soak test. The application also remains responsive, further confirms the fix works.

      What we can't explain is why killing the callback timer immediately before starting a new timer fixes the leak, although doing so makes more sense anyway. I would have expected queuing two lamda slot calls should have them executed in the order specified by the code, does that assumption not hold? The other improvement in the patch is to switch fallback update timer to QAtomicInt as well, although it's not clear whether that timer is also contributing to the timer leak.

      Attachments

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

        Activity

          People

            e0150566 Janne Koskinen
            siyuan.navico Simon Yuan
            Votes:
            1 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes