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

QSoundEffect is stuck in Loading state

    XMLWordPrintable

Details

    • All
    • cb5f13e76 (dev), f53f31d9b (6.5)

    Description

      QSoundEffect might fail to load when the source is being set for multiple effects with a short time interval.

       

      The last call of 'QSoundEffect::setSource()' might cause that its related instance of QSoundEffect will stuck in the state 'QSoundEffect::Loading'.

       

      Impact

      1. There is no sound for the last configured instance of QSoundEffect both in Qt5 and Qt6 versions
      2. The application might freeze on destruction of QSoundEffect in Linux Qt5 with pulse backend
      3. 3. QSampleCache resource leak in Qt5 without pulse audio or in Qt6.

       

      Details

       

      There is a race between 'QSampleCache::requestSample()' of the main thread and 'QSampleCache::loadingRelease()' of 'm_loadingThread'.

       

      The 'QThread::isRunning()' state is not updated immediately after calling 'QThread::exit()'.

      • It is possible that 'QSampleCache::loadingRelease()' will initiate exit of the loading thread, since all sources are already loaded (‘m_loadingRefCount == 0’).
      • Then the next call of 'QSampleCache::requestSample()' ('QSoundEffect::setSource()') will see that the loading thread is still running while the thread is being stopped.

       

      1. It results in incrementing 'm_loadingRefCount' for the effect that will not be able to finish its loading, since the loader thread is not running.

       

      2. In Qt5 with pulse audio if all instances of QSoundEffect are destroyed in the above state the counter 'm_loadingRefCount' value will be 1. Once ‘QSoundEffect::setSource’ is called for a new sound instance the loading thread will be always running, since the ref count cannot become 0.

      During destruction of the newly created sounds the application will freeze in 'QSampleCache::notifyUnreferencedSample()' waiting for the loader thread.

       

      3. In Qt6 'QSoundEffectPrivate::release()' does not call 'd->m_sample->release()' if the sample loading is not finished. The sample is not removed from 'QSampleCache'. This sample might finish its loading after configuring a new sound. However, this sample will stay in the cache for the entire application run time.

       

      The bug was reproduced in Linux with versions 5.14.2 and 6.2.4, however, it looks that the same issues can happen in other operating systems and other newer versions.

       

      An example of application for reproducing the bug is attached: soundeffect_race_demo.tar.bz2

       

      Once the button 'Play' is clicked the application will continuously run in a loop configuring sources for 10 instances of QSoundEffect and then destroying them.

      The reproducibility depends on the system load. There is a busy loop in the demo application between ‘setSource’ calls.

      Few seconds is enough to reproduce the bug in case of a good timing.

      The word 'Timeout' is printed to console. Then the application demonstrates that the last QSoundEffect cannot produce sound and its status is 'Loading'.

       

      Additionally in Linux with Qt5 if the 'Play' button is pressed again after the timeout the application will freeze.

       

      The fix is to check in 'requestSample()' if the loading thread is scheduled to be stopped. Then wait for the previous thread execution to finish before starting the thread again. The patches are attached for Qt5 and Qt6:

      0001-QSampleCache-Qt5-Fix-starting-loading-thread.patch
      0001-QSampleCache-Qt6-Fix-starting-loading-thread.patch

      Attachments

        Issue Links

          For Gerrit Dashboard: QTBUG-109167
          # Subject Branch Project Status CR V

          Activity

            People

              mikolaj.boc Mikołaj Boc (Inactive)
              orest_sl Orest Hera
              Votes:
              1 Vote for this issue
              Watchers:
              6 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes