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

[wasm] Event handling reentrant occurred with multi-threaded qt-wasm 5.15

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • P2: Important
    • 6.4
    • 5.15.2
    • Core: Event loop
    • None
    • qt-5.15
      emscripten 2.0.8
      ubuntu 18.04
    • WebAssembly

    Description

          Recently We found some deadlocks in our web-assembly app, after some investigation I found that the deadlock is caused by reentrant of Qt event handling with multi-threaded qt-wasm 5.15. I build qt-5.15 from git with emscripten 2.0.8 with thread enabled.

          Below is a callstack when deadlock happened:

      _emscripten_futex_wait (VM12:8491)
      __timedwait_cp (type_traits:2261)
      __timedwait (type_traits:2261)
      __pthread_mutex_timedlock (type_traits:2261)
      __pthread_mutex_lock (type_traits:2261)
      std::_2::_libcpp_mutex_lock(pthread_mutex_t*) (type_traits:2261)
      std::__2::mutex::lock() (type_traits:2261)
      std::_2::lock_guard<std::2::mutex>::lock_guard(std::2::mutex&) (_mutex_base:91)
      CNvPlatformEventHandler::SendPostedEventByEventHandlerId(long long) (NvPlatformEventHandler.cpp:246)
      CNvQtEventHandler::customEvent(QEvent*) (NvPlatformEventHandler.cpp:115)
      QObject::event(QEvent*) (type_traits:2261)
      QCoreApplicationPrivate::notify_helper(QObject*, QEvent*) (type_traits:2261)
      QCoreApplication::notify(QObject*, QEvent*) (type_traits:2261)
      QGuiApplication::notify(QObject*, QEvent*) (type_traits:2261)
      QCoreApplication::notifyInternal2(QObject*, QEvent*) (type_traits:2261)
      QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (type_traits:2261)
      QEventDispatcherUNIX::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (type_traits:2261)
      QUnixEventDispatcherQPA::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (type_traits:2261)
      QWasmEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (type_traits:2261)
      QWasmEventDispatcher::mainThreadWakeUp(void*) (type_traits:2261)
      _do_call (type_traits:2261)
      emscripten_current_thread_process_queued_calls (type_traits:2261)
      emscripten_main_thread_process_queued_calls (type_traits:2261)
      __timedwait_cp (type_traits:2261)
      __timedwait (type_traits:2261)
      __pthread_mutex_timedlock (type_traits:2261)
      __pthread_mutex_lock (type_traits:2261)
      dlfree (type_traits:2261)
      SNvArrayData::deallocate(SNvArrayData*, unsigned long, unsigned long) (NvArrayData.cpp:131)
      SNvTypedArrayData<unsigned short>::deallocate(SNvArrayData*) (NvArrayData.h:134)
      CNvString::~CNvString() (NvString.h:786)
      CNvDebugLog::Stream::~Stream() (NvDebug.h:101)
      CNvDebugLog::DropStream() (NvDebug.cpp:372)
      CNvDebugLog::~CNvDebugLog() (NvDebug.cpp:128)
      CNvWebReaderManager::TryAbortWebRequest() (NvWebReaderManager.cpp:658)
      CNvWebReaderManager::SendWebRequest(CNvBaseWebReader*, int, int, bool) (NvWebReaderManager.cpp:609)
      CNvWebReaderManager::ProcessEvent(CNvEvent*) (NvWebReaderManager.cpp:422)
      CNvPlatformEventHandler::SendPostedEvent(int) (NvPlatformEventHandler.cpp:217)
      CNvPlatformEventHandler::SendPostedEventByEventHandlerId(long long) (NvPlatformEventHandler.cpp:249)
      CNvQtEventHandler::customEvent(QEvent*) (NvPlatformEventHandler.cpp:115)
      QObject::event(QEvent*) (type_traits:2261)
      QCoreApplicationPrivate::notify_helper(QObject*, QEvent*) (type_traits:2261)
      QCoreApplication::notify(QObject*, QEvent*) (type_traits:2261)
      QGuiApplication::notify(QObject*, QEvent*) (type_traits:2261)
      QCoreApplication::notifyInternal2(QObject*, QEvent*) (type_traits:2261)
      QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (type_traits:2261)
      QEventDispatcherUNIX::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (type_traits:2261)
      QUnixEventDispatcherQPA::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (type_traits:2261)
      QWasmEventDispatcher::doMaintainTimers()::$1::_invoke(void*) (type_traits:2261)

          The scenario is that we post QEvent from a child thread to the main thread to trigger main thread working on a work queue, in the main thread's qt event handler we will do work in the work queue one by one with a lock held. To my surprise QWasmEventDispatcher::processEvents() get called while we do work of the queue as you can see in the callstack:

      QWasmEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (type_traits:2261)
      QWasmEventDispatcher::mainThreadWakeUp(void*) (type_traits:2261)
      _do_call (type_traits:2261)
      emscripten_current_thread_process_queued_calls (type_traits:2261)
      emscripten_main_thread_process_queued_calls (type_traits:2261)
      __timedwait_cp (type_traits:2261)
      __timedwait (type_traits:2261)
      __pthread_mutex_timedlock (type_traits:2261)
      __pthread_mutex_lock (type_traits:2261)
      dlfree (type_traits:2261)

          That is to say: when I call ::free() C runtime function implemented by emscritpen, emscritpen will call emscripten_main_thread_process_queued_calls() in the mean time. (I think this is deliberately since emscripten always proxy some work to main thread which is called from child thread) But that trigger a QWasmEventDispatcher::mainThreadWakeUp() function call and then QWasmEventDispatcher::processEvents() get called. A reentrant happened!
          I know I can solve my deadlock with a very simple solution, But I think the more serious problem is Qt event handling reentrant which will cause unexpected problems.

      Attachments

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

        Activity

          People

            sorvig Morten Sørvig
            jianliang79 liang jian
            Veli-Pekka Heinonen Veli-Pekka Heinonen
            Votes:
            2 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes