Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QApplication in std::thread
QtWS25 Last Chance

QApplication in std::thread

Scheduled Pinned Locked Moved Solved General and Desktop
22 Posts 3 Posters 3.9k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • JKSHJ Offline
    JKSHJ Offline
    JKSH
    Moderators
    wrote on last edited by JKSH
    #2

    Hi @Suthiro, and welcome! First, thank you for such a detailed investigation and report. We appreciate your efforts.

    Now, onto the core of your problem:

    @Suthiro said in QApplication in std::thread:

    The message itself does not bother me, but the third party program crashes with access violation and other "pointers to a deep space" funky stuff.

    I believe the message and the crash are actually 2 separate things.

    Ignore the message for now and see if you can get a stack trace for the crash. I haven't seen "pointers to a deep space" before -- Can you post the exact error messages?

    If that's too hard, then see if manually deleting all widgets before quitting helps:

    // IMPORTANT: Make sure that all widgets are heap-allocated, not stack-allocated.
    // Call this from your main thread when you're ready to quit.
    QMetaObject::invokeMethod(qApp, []
    {
        // Prevent auto-quitting, just in case
        qApp->setQuitOnLastWindowClosed(false);
    
        for (auto w : qApp->topLevelWidgets())
            w->deleteLater();
    
    }, Qt::BlockingQueuedConnection);
    
    QMetaObject::invokeMethod(qApp, &QApplication::quit, Qt::BlockingQueuedConnection);
    

    EDIT: I see on StackOverflow that you have delete m_qPlotsControl; Some complex widgets don't like direct deletion.

    • Quick and dirty hack: Comment out delete m_qPlotsControl; -- Does this stop the crash?
    • Proper fix: Ensure that your widget is fully destroyed with m_qPlotsControl->deleteLater() before QApplication::exec() returns.

    I narrowed down the problem to an extremely simple example:

    I confirm that I can run your example and reproduce the message in Qt 5.12.10 MSVC 2019 64-bit, Qt 5.14.2 MinGW 32-bit, and Qt 5.15.2 MSVC 2019 64-bit. (Note: my MSVC didn't like char *argv[] = { "gui\0" };)

    But importantly, none of them crashed.

    As I understand from links above, some QObject is created before QApplication. I kindly ask for your help to understand where it is created...

    In your example, the message was not produced because some QObject is created before QApplication. Rather, I believe that complex widgets (indirectly?) create global timers in the background, but these timers are not destroyed when the original widget or even the QApplication are destroyed.

    The timers only get destroyed when the they get unloaded from memory, at which point they rightfully complain about being unloaded from a different thread.

    ...or to confirm that now it is not possible to run a QApplication in "non-main" thread.

    It is definitely possible to run a QApplication in "non-main" thread.

    I too have a DLL that creates a QApplication in a std::thread, and I can do all kinds of complex things with widgets (e.g. use buttons and comboboxes, delete the original QApplication and create a new one, etc.). No crashes at all.

    However, I do get the message QObject::~QObject: Timers cannot be stopped from another thread when the 3rd party app that loaded the DLL shuts down; the message seems harmless and the process still returns 0, which indicates a safe and successful shutdown.

    If you're interested:

    • My project is https://github.com/JKSH/LQ-Bindings/
    • The thread code is at https://github.com/JKSH/LQ-Bindings/blob/master/src/Cpp/lqmain.cpp (Ignore the multiple layers of mutexes/locks/wait-conditions; in my case the 3rd party application is multithreaded and can call my DLL functions from any arbitrary thread)

    on Stackoverflow: starting-qt-gui-from-dll-in-dllstart-function
    There is a suggestion to use QThread, I've tried. The window could not be closed, the button is not interactable at all, qApp never returns from exec, and WARNING: QApplication was not created in the main() thread. is issued.

    Nope, QThread definitely causes problems. QApplication must be the very first QObject to be created, so you must not create a QThread before a QApplication.

    Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

    1 Reply Last reply
    4
    • S Suthiro

      Hello!
      I created a dll with non-blocking loading function. It is a plugin to a third-party program.
      The idea was to create QApplication inside a std::thread routine. Everything worked smoothly, until I have added a QPushButton to the layout. Now, if the button was interacted, including simple hovering over, a message stating that QObject::~QObject: Timers cannot be stopped from another thread is printed when the QApplication returns from exec(). If the button is not interacted, everything is ok. The message itself does not bother me, but the third party program crashes with access violation and other "pointers to a deep space" funky stuff.

      I narrowed down the problem to an extremely simple example:

      #include <QApplication>
      #include <QPushButton>
      #include <thread>
      
      void thrRoutine()
      {
      	int argc = 1;
      	char *argv[] = { "gui\0" };
      	QApplication a(argc, argv);
      	QPushButton btn("Push");
      	btn.show();
      	a.exec();
      }
      
      int main(int argc, char *argv[])
      {
      	std::thread thr(thrRoutine);
      	thr.join();
      }
      

      Please note that it is not even a dll, an executable is built. This is enough to reproduce the aforementioned message. Just hover few times over the button or click it. What I've found on topic so far:
      qapplication-in-library-project
      looks like OP gave up.

      qapplication-in-a-std-thread-that-lives-in-a-dll
      The code is complicated, and it is not clear if the solution was provided. I tried to use parts of this code to check if something helps - but it doesn't, unfortunately.

      how-to-use-qapplication-on-a-separate-thread-other-than-the-main-thread
      There is a statement that this is (was?) possible.

      running-gui-in-thread-other-than-main-thread-qobject-error-timers-cannot-be-stopped-from-another-thread
      contains the exact message, and there is a suggestion to check whether static QObjects are present, but in the example I posted there are no indeed.

      on Stackoverflow: starting-qt-gui-from-dll-in-dllstart-function
      There is a suggestion to use QThread, I've tried. The window could not be closed, the button is not interactable at all, qApp never returns from exec, and WARNING: QApplication was not created in the main() thread. is issued.

      on Stackoverflow: weird-qpushbutton-bug
      this is my question with some additional background and commenters stating that one is not able to create a QApplication gui in a thread other than "main".

      I use MSVC 2017 (toolset v141) on WIndows 7 x64. Compiled qt5.14.1 from source, but also tried some pre-built binaries.

      As I understand from links above, some QObject is created before QApplication. I kindly ask for your help to understand where it is created or to confirm that now it is not possible to run a QApplication in "non-main" thread.
      Thank you very much.

      kshegunovK Offline
      kshegunovK Offline
      kshegunov
      Moderators
      wrote on last edited by kshegunov
      #3

      @Suthiro did you follow the threads?
      Specifically this:
      https://forum.qt.io/topic/74520/running-gui-in-thread-other-than-main-thread-qobject-error-timers-cannot-be-stopped-from-another-thread/3
      leads to:
      https://forum.qt.io/topic/67285/ui-application-as-thread-cpu-load-almost-100/11

      Which if you notice passes the argc and argv along from main() to the thread. You should do that too.

      Read and abide by the Qt Code of Conduct

      JKSHJ 1 Reply Last reply
      0
      • kshegunovK kshegunov

        @Suthiro did you follow the threads?
        Specifically this:
        https://forum.qt.io/topic/74520/running-gui-in-thread-other-than-main-thread-qobject-error-timers-cannot-be-stopped-from-another-thread/3
        leads to:
        https://forum.qt.io/topic/67285/ui-application-as-thread-cpu-load-almost-100/11

        Which if you notice passes the argc and argv along from main() to the thread. You should do that too.

        JKSHJ Offline
        JKSHJ Offline
        JKSH
        Moderators
        wrote on last edited by
        #4

        @kshegunov said in QApplication in std::thread:

        @Suthiro did you follow the threads?
        Specifically this:
        https://forum.qt.io/topic/74520/running-gui-in-thread-other-than-main-thread-qobject-error-timers-cannot-be-stopped-from-another-thread/3
        leads to:
        https://forum.qt.io/topic/67285/ui-application-as-thread-cpu-load-almost-100/11

        My point above is that @Suthiro's crash is not directly related to the message, "QObject::~QObject: Timers cannot be stopped from another thread". We can fix the crash, but I think we can't get rid of this message at shutdown if QPushButtons were interacted with.

        @Suthiro's sample code (see original post) definitely does not contain static QObjects (or any QObject created before QApplication).

        Which if you notice passes the argc and argv along from main() to the thread. You should do that too.

        Passing argc and argv from main() into QApplication doesn't get rid of the message, and I doubt it would prevent the crash.

        Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

        kshegunovK 1 Reply Last reply
        0
        • JKSHJ JKSH

          @kshegunov said in QApplication in std::thread:

          @Suthiro did you follow the threads?
          Specifically this:
          https://forum.qt.io/topic/74520/running-gui-in-thread-other-than-main-thread-qobject-error-timers-cannot-be-stopped-from-another-thread/3
          leads to:
          https://forum.qt.io/topic/67285/ui-application-as-thread-cpu-load-almost-100/11

          My point above is that @Suthiro's crash is not directly related to the message, "QObject::~QObject: Timers cannot be stopped from another thread". We can fix the crash, but I think we can't get rid of this message at shutdown if QPushButtons were interacted with.

          @Suthiro's sample code (see original post) definitely does not contain static QObjects (or any QObject created before QApplication).

          Which if you notice passes the argc and argv along from main() to the thread. You should do that too.

          Passing argc and argv from main() into QApplication doesn't get rid of the message, and I doubt it would prevent the crash.

          kshegunovK Offline
          kshegunovK Offline
          kshegunov
          Moderators
          wrote on last edited by
          #5

          @JKSH said in QApplication in std::thread:

          Passing argc and argv from main() into QApplication doesn't get rid of the message, and I doubt it would prevent the crash.

          Can you give me a stack trace from that message? It didn't used to be the case that one gets that, so unless something changed, one shouldn't. Qt shouldn't at all know what is the "main" thread to begin with. The QCoreApplication adopts the thread it was started in and marks it as the main one, so it should be oblivious to the fact it is not in main() as such.

          Read and abide by the Qt Code of Conduct

          JKSHJ 1 Reply Last reply
          0
          • kshegunovK kshegunov

            @JKSH said in QApplication in std::thread:

            Passing argc and argv from main() into QApplication doesn't get rid of the message, and I doubt it would prevent the crash.

            Can you give me a stack trace from that message? It didn't used to be the case that one gets that, so unless something changed, one shouldn't. Qt shouldn't at all know what is the "main" thread to begin with. The QCoreApplication adopts the thread it was started in and marks it as the main one, so it should be oblivious to the fact it is not in main() as such.

            JKSHJ Offline
            JKSHJ Offline
            JKSH
            Moderators
            wrote on last edited by JKSH
            #6

            @kshegunov said in QApplication in std::thread:

            Can you give me a stack trace...?

            That's what I asked @Suthiro too :-) I don't have any crashing code to work with.

            My guess: The crash was caused by delete m_qPlotsControl; (see https://stackoverflow.com/questions/67257329/weird-qpushbutton-bug ). I believe it is fixable with m_qPlotsControl->deleteLater();... but that needs to be done before QApplication::exec() returns, because QDeferredDeleteEvent won't be processed without an event loop.

            Qt shouldn't at all know what is the "main" thread to begin with. The QCoreApplication adopts the thread it was started in and marks it as the main one, so it should be oblivious to the fact it is not in main() as such.

            Agreed. And I believe this hasn't changed.

            @Suthiro's issues are not about which thread Qt marks as "main". Rather, I believe the issue is this:

            • Interacting with a QPushButton caused a timer to be created in the background by the Qt thread.
            • This timer is not stopped/destroyed when the QPushButton or QApplication are destroyed.
            • When main() is about to return, the program tries to unload the timer. But the unloading is done by the main() thread, not the Qt thread. Hence the complaint, "QObject::~QObject: Timers cannot be stopped from another thread".

            Important detail: If we don't interact with the QPushButton, then we don't get the message about timers.

            Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

            kshegunovK 1 Reply Last reply
            0
            • JKSHJ JKSH

              @kshegunov said in QApplication in std::thread:

              Can you give me a stack trace...?

              That's what I asked @Suthiro too :-) I don't have any crashing code to work with.

              My guess: The crash was caused by delete m_qPlotsControl; (see https://stackoverflow.com/questions/67257329/weird-qpushbutton-bug ). I believe it is fixable with m_qPlotsControl->deleteLater();... but that needs to be done before QApplication::exec() returns, because QDeferredDeleteEvent won't be processed without an event loop.

              Qt shouldn't at all know what is the "main" thread to begin with. The QCoreApplication adopts the thread it was started in and marks it as the main one, so it should be oblivious to the fact it is not in main() as such.

              Agreed. And I believe this hasn't changed.

              @Suthiro's issues are not about which thread Qt marks as "main". Rather, I believe the issue is this:

              • Interacting with a QPushButton caused a timer to be created in the background by the Qt thread.
              • This timer is not stopped/destroyed when the QPushButton or QApplication are destroyed.
              • When main() is about to return, the program tries to unload the timer. But the unloading is done by the main() thread, not the Qt thread. Hence the complaint, "QObject::~QObject: Timers cannot be stopped from another thread".

              Important detail: If we don't interact with the QPushButton, then we don't get the message about timers.

              kshegunovK Offline
              kshegunovK Offline
              kshegunov
              Moderators
              wrote on last edited by kshegunov
              #7

              @JKSH said in QApplication in std::thread:

              That's what I asked @Suthiro too :-) I don't have any crashing code to work with.

              I meant the trace from QT_FATAL_WARNINGS, so we could see what was called leading to the mentioned warning.

              @JKSH said in QApplication in std::thread:

              When main() is about to return, the program tries to unload the timer. But the unloading is done by the main() thread, not the Qt thread. Hence the complaint, "QObject::~QObject: Timers cannot be stopped from another thread".

              I really doubt this as the runtime doesn't run your destructors automatically. In any case if that is the case, then it's a bug in the push button, which should be posted on the tracker.

              Read and abide by the Qt Code of Conduct

              JKSHJ 1 Reply Last reply
              0
              • S Offline
                S Offline
                Suthiro
                wrote on last edited by Suthiro
                #8

                Thank to you all very much for your help and time! Since there are 22 (!) replies I'm sorry in advance if I missed something I need to reply to.

                @JKSH

                Quick and dirty hack: Comment out delete m_qPlotsControl; -- Does this stop the crash?

                I tried to delete, not to delete, to allocate everything (QApplication, QPushButton, QPlotsControl) on heap and on stack in all possible combinations. Crash persists.

                Ignore the message for now and see if you can get a stack trace for the crash. I haven't seen "pointers to a deep space" before -- Can you post the exact error messages?

                "An exception at address 0x000007FED396B168 raised in lua.exe: 0xC0000005: access rights violation during execution at 0x000007FED396B168." (original in Russian :"Вызвано исключение по адресу 0x000007FED396B168 в lua.exe: 0xC0000005: нарушение прав доступа при исполнении по адресу 0x000007FED396B168.")

                Stack trace:

                000007fed396b168()
                kernel32.dll!000000007702556d()
                ntdll.dll!000000007718372d()
                

                Thread: Win64 (working thread). It is neither the thread where QApplication is created, nor the "main" thread of the dll.
                Addresses, of course, change from run to run.

                Crash will not occur if the button was not interacted. A QTabWidget with custom widgets and buttons and whatever is interactable without problems.
                About "pointers to a deep space": I meant dangling pointers, sorry for not being serious & clear.

                // IMPORTANT: Make sure that all widgets are heap-allocated, not stack-allocated.
                // Call this from your main thread when you're ready to quit.

                the message about timers is issued, but no crash occurs. It is really great!

                Also, it looks like that only top-level widget must be heap allocated. QPushButton could be a stack-allocated member variable.
                ctor contains now exactly one line aside the initializers:

                QPushButton QPlotsControl::m_Btn;
                QPlotsControl::QPlotsControl(const WorkerThreadCallbackFn& cbFunc, const readyCallbackFn& readyCb,
                	QWidget *parent) : m_Callback{ cbFunc }, m_readyCallback { readyCb },
                	m_needsClosing{false},
                	QMainWindow(parent) { m_Btn.setParent(this); }
                

                Again, no crash if QMetaObject::invokeMethod is used. I think that children are destroyed properly.

                Important detail: If we don't interact with the QPushButton, then we don't get the message about timers.

                And no crashes!

                I believe it is fixable with m_qPlotsControl->deleteLater();... but that needs to be done before QApplication::exec() returns, because QDeferredDeleteEvent won't be processed without an event loop.

                It is fixable, thank you!

                Since the widget creates a timer in the background, then it's probably receiving background events/signals. delete could cause the event/signal handler to act on a dangling pointer -- that's how it could generate a crash. And that's why deleteLater() can fix the crash. (Alternatively, we could change from a heap-allocated widget to a stack-allocated widget)

                Stack allocated widget (actually, even all of them) is not enough. Just re-checked. I do really need to call that QMetaObject::invokeMethod to close the QApplication without crash.

                @kshegunov

                did you follow the threads?

                Yes, I did. The link that you've provided points to the code which is very similiar if not identical to that I use: create QApplication in a std::thread.

                Which if you notice passes the argc and argv along from main() to the thread. You should do that too.

                There is no argc/argv in a library.

                Can you give me a stack trace from that message?

                Please, see above.

                @Chris-Kawa

                I would consider this a Qt bug and expect it to crash all over the place.

                @kshegunov

                Yes, this is constructed appropriately, however the deallocation should've been tied to the QCoreApplication::aboutToQuit. I doubt anyone is going to bother fixing it though, I'm not sure if it's not going to be discarded with "this is unsupported" even ...

                @JKSH

                Me too, which is what I wrote somewhere upstairs - this should be fixed at the vendor side.

                As I understand, this is a bug in Qt after all. A workaround is to use QMetaObject::invokeMethod described by @JKSH. But are there any side effects?

                Thank you all once again!

                kshegunovK JKSHJ 3 Replies Last reply
                0
                • S Suthiro

                  Thank to you all very much for your help and time! Since there are 22 (!) replies I'm sorry in advance if I missed something I need to reply to.

                  @JKSH

                  Quick and dirty hack: Comment out delete m_qPlotsControl; -- Does this stop the crash?

                  I tried to delete, not to delete, to allocate everything (QApplication, QPushButton, QPlotsControl) on heap and on stack in all possible combinations. Crash persists.

                  Ignore the message for now and see if you can get a stack trace for the crash. I haven't seen "pointers to a deep space" before -- Can you post the exact error messages?

                  "An exception at address 0x000007FED396B168 raised in lua.exe: 0xC0000005: access rights violation during execution at 0x000007FED396B168." (original in Russian :"Вызвано исключение по адресу 0x000007FED396B168 в lua.exe: 0xC0000005: нарушение прав доступа при исполнении по адресу 0x000007FED396B168.")

                  Stack trace:

                  000007fed396b168()
                  kernel32.dll!000000007702556d()
                  ntdll.dll!000000007718372d()
                  

                  Thread: Win64 (working thread). It is neither the thread where QApplication is created, nor the "main" thread of the dll.
                  Addresses, of course, change from run to run.

                  Crash will not occur if the button was not interacted. A QTabWidget with custom widgets and buttons and whatever is interactable without problems.
                  About "pointers to a deep space": I meant dangling pointers, sorry for not being serious & clear.

                  // IMPORTANT: Make sure that all widgets are heap-allocated, not stack-allocated.
                  // Call this from your main thread when you're ready to quit.

                  the message about timers is issued, but no crash occurs. It is really great!

                  Also, it looks like that only top-level widget must be heap allocated. QPushButton could be a stack-allocated member variable.
                  ctor contains now exactly one line aside the initializers:

                  QPushButton QPlotsControl::m_Btn;
                  QPlotsControl::QPlotsControl(const WorkerThreadCallbackFn& cbFunc, const readyCallbackFn& readyCb,
                  	QWidget *parent) : m_Callback{ cbFunc }, m_readyCallback { readyCb },
                  	m_needsClosing{false},
                  	QMainWindow(parent) { m_Btn.setParent(this); }
                  

                  Again, no crash if QMetaObject::invokeMethod is used. I think that children are destroyed properly.

                  Important detail: If we don't interact with the QPushButton, then we don't get the message about timers.

                  And no crashes!

                  I believe it is fixable with m_qPlotsControl->deleteLater();... but that needs to be done before QApplication::exec() returns, because QDeferredDeleteEvent won't be processed without an event loop.

                  It is fixable, thank you!

                  Since the widget creates a timer in the background, then it's probably receiving background events/signals. delete could cause the event/signal handler to act on a dangling pointer -- that's how it could generate a crash. And that's why deleteLater() can fix the crash. (Alternatively, we could change from a heap-allocated widget to a stack-allocated widget)

                  Stack allocated widget (actually, even all of them) is not enough. Just re-checked. I do really need to call that QMetaObject::invokeMethod to close the QApplication without crash.

                  @kshegunov

                  did you follow the threads?

                  Yes, I did. The link that you've provided points to the code which is very similiar if not identical to that I use: create QApplication in a std::thread.

                  Which if you notice passes the argc and argv along from main() to the thread. You should do that too.

                  There is no argc/argv in a library.

                  Can you give me a stack trace from that message?

                  Please, see above.

                  @Chris-Kawa

                  I would consider this a Qt bug and expect it to crash all over the place.

                  @kshegunov

                  Yes, this is constructed appropriately, however the deallocation should've been tied to the QCoreApplication::aboutToQuit. I doubt anyone is going to bother fixing it though, I'm not sure if it's not going to be discarded with "this is unsupported" even ...

                  @JKSH

                  Me too, which is what I wrote somewhere upstairs - this should be fixed at the vendor side.

                  As I understand, this is a bug in Qt after all. A workaround is to use QMetaObject::invokeMethod described by @JKSH. But are there any side effects?

                  Thank you all once again!

                  kshegunovK Offline
                  kshegunovK Offline
                  kshegunov
                  Moderators
                  wrote on last edited by kshegunov
                  #9

                  I am now very confused. How and why QMetaObject::invokeMethod works, what it calls and what it fixes ...?

                  @Suthiro said in QApplication in std::thread:

                  Thread: Win64 (working thread). It is neither the thread where QApplication is created, nor the "main" thread of the dll.
                  Addresses, of course, change from run to run.

                  You receive a SIGSEGV, which is in a thread that you don't own, it's a system one from the kernel. This means nothing.

                  There is no argc/argv in a library.

                  Which is also why QApplication instances should not be created in a library. Please describe what you're doing, and why! I admit we got carried away tracing the technical problem, but it does sound to me you're doing something very wrong ideologically.

                  Read and abide by the Qt Code of Conduct

                  JKSHJ 1 Reply Last reply
                  0
                  • kshegunovK kshegunov

                    I am now very confused. How and why QMetaObject::invokeMethod works, what it calls and what it fixes ...?

                    @Suthiro said in QApplication in std::thread:

                    Thread: Win64 (working thread). It is neither the thread where QApplication is created, nor the "main" thread of the dll.
                    Addresses, of course, change from run to run.

                    You receive a SIGSEGV, which is in a thread that you don't own, it's a system one from the kernel. This means nothing.

                    There is no argc/argv in a library.

                    Which is also why QApplication instances should not be created in a library. Please describe what you're doing, and why! I admit we got carried away tracing the technical problem, but it does sound to me you're doing something very wrong ideologically.

                    JKSHJ Offline
                    JKSHJ Offline
                    JKSH
                    Moderators
                    wrote on last edited by JKSH
                    #10

                    @kshegunov said in QApplication in std::thread:

                    I am now very confused. How and why QMetaObject::invokeMethod works, what it calls and what it fixes ...?

                    Please read my very first post in this thread.

                    Which is also why QApplication instances should not be created in a library. Please describe what you're doing, and why! I admit we got carried away tracing the technical problem, but it does sound to me you're doing something very wrong ideologically.

                    It's uncommon for sure, but it's certainly a valid use-case.

                    Judging from @Suthiro's error message ("An exception at address 0x000007FED396B168 raised in lua.exe), he is making a Qt-based plotting library to be used in a Lua program. Since Lua is a different language, the C++ bits need to be wrapped up in a library. And since Lua controls the main thread, the blocking event loop needs to be shoved into a secondary thread.

                    Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                    1 Reply Last reply
                    0
                    • S Suthiro

                      Thank to you all very much for your help and time! Since there are 22 (!) replies I'm sorry in advance if I missed something I need to reply to.

                      @JKSH

                      Quick and dirty hack: Comment out delete m_qPlotsControl; -- Does this stop the crash?

                      I tried to delete, not to delete, to allocate everything (QApplication, QPushButton, QPlotsControl) on heap and on stack in all possible combinations. Crash persists.

                      Ignore the message for now and see if you can get a stack trace for the crash. I haven't seen "pointers to a deep space" before -- Can you post the exact error messages?

                      "An exception at address 0x000007FED396B168 raised in lua.exe: 0xC0000005: access rights violation during execution at 0x000007FED396B168." (original in Russian :"Вызвано исключение по адресу 0x000007FED396B168 в lua.exe: 0xC0000005: нарушение прав доступа при исполнении по адресу 0x000007FED396B168.")

                      Stack trace:

                      000007fed396b168()
                      kernel32.dll!000000007702556d()
                      ntdll.dll!000000007718372d()
                      

                      Thread: Win64 (working thread). It is neither the thread where QApplication is created, nor the "main" thread of the dll.
                      Addresses, of course, change from run to run.

                      Crash will not occur if the button was not interacted. A QTabWidget with custom widgets and buttons and whatever is interactable without problems.
                      About "pointers to a deep space": I meant dangling pointers, sorry for not being serious & clear.

                      // IMPORTANT: Make sure that all widgets are heap-allocated, not stack-allocated.
                      // Call this from your main thread when you're ready to quit.

                      the message about timers is issued, but no crash occurs. It is really great!

                      Also, it looks like that only top-level widget must be heap allocated. QPushButton could be a stack-allocated member variable.
                      ctor contains now exactly one line aside the initializers:

                      QPushButton QPlotsControl::m_Btn;
                      QPlotsControl::QPlotsControl(const WorkerThreadCallbackFn& cbFunc, const readyCallbackFn& readyCb,
                      	QWidget *parent) : m_Callback{ cbFunc }, m_readyCallback { readyCb },
                      	m_needsClosing{false},
                      	QMainWindow(parent) { m_Btn.setParent(this); }
                      

                      Again, no crash if QMetaObject::invokeMethod is used. I think that children are destroyed properly.

                      Important detail: If we don't interact with the QPushButton, then we don't get the message about timers.

                      And no crashes!

                      I believe it is fixable with m_qPlotsControl->deleteLater();... but that needs to be done before QApplication::exec() returns, because QDeferredDeleteEvent won't be processed without an event loop.

                      It is fixable, thank you!

                      Since the widget creates a timer in the background, then it's probably receiving background events/signals. delete could cause the event/signal handler to act on a dangling pointer -- that's how it could generate a crash. And that's why deleteLater() can fix the crash. (Alternatively, we could change from a heap-allocated widget to a stack-allocated widget)

                      Stack allocated widget (actually, even all of them) is not enough. Just re-checked. I do really need to call that QMetaObject::invokeMethod to close the QApplication without crash.

                      @kshegunov

                      did you follow the threads?

                      Yes, I did. The link that you've provided points to the code which is very similiar if not identical to that I use: create QApplication in a std::thread.

                      Which if you notice passes the argc and argv along from main() to the thread. You should do that too.

                      There is no argc/argv in a library.

                      Can you give me a stack trace from that message?

                      Please, see above.

                      @Chris-Kawa

                      I would consider this a Qt bug and expect it to crash all over the place.

                      @kshegunov

                      Yes, this is constructed appropriately, however the deallocation should've been tied to the QCoreApplication::aboutToQuit. I doubt anyone is going to bother fixing it though, I'm not sure if it's not going to be discarded with "this is unsupported" even ...

                      @JKSH

                      Me too, which is what I wrote somewhere upstairs - this should be fixed at the vendor side.

                      As I understand, this is a bug in Qt after all. A workaround is to use QMetaObject::invokeMethod described by @JKSH. But are there any side effects?

                      Thank you all once again!

                      JKSHJ Offline
                      JKSHJ Offline
                      JKSH
                      Moderators
                      wrote on last edited by
                      #11

                      @Suthiro said in QApplication in std::thread:

                      the message about timers is issued, but no crash occurs. It is really great!

                      Wonderful! :-)

                      Please mark this thread as "Solved".

                      As I understand, this is a bug in Qt after all. A workaround is to use QMetaObject::invokeMethod described by @JKSH. But are there any side effects?

                      The only side-effect I can think of is that you need to maintain a few more lines of code.

                      My code simply calls deleteLater() on every top-level widget in your program (this automatically deletes child widgets too), before quitting the QApplication.

                      Please help me test a simpler version (Comment out lines #1 and #2 below to trigger automatic quitting via quitOnLastWindowClosed) -- does this still work?:

                      // IMPORTANT: Make sure that all widgets are heap-allocated, not stack-allocated.
                      // Call this from your main thread when you're ready to quit.
                      QMetaObject::invokeMethod(qApp, []
                      {
                          // qApp->setQuitOnLastWindowClosed(false); // #1
                      
                          for (auto w : qApp->topLevelWidgets())
                              w->deleteLater();
                      
                      }, Qt::BlockingQueuedConnection);
                      
                      // QMetaObject::invokeMethod(qApp, &QApplication::quit, Qt::BlockingQueuedConnection); // #2
                      

                      I tried to delete, not to delete, to allocate everything (QApplication, QPushButton, QPlotsControl) on heap and on stack in all possible combinations. Crash persists.

                      ...

                      Stack allocated widget (actually, even all of them) is not enough. Just re-checked. I do really need to call that QMetaObject::invokeMethod to close the QApplication without crash.

                      I see, thank you for testing.

                      	QMainWindow(parent) { m_Btn.setParent(this); }
                      

                      Oh, please don't call setParent() on a widget that is not created with new. It can cause a different type of crash because the widget might get deleted twice.

                      See https://doc.qt.io/qt-5/objecttrees.html#construction-destruction-order-of-qobjects

                      Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                      1 Reply Last reply
                      0
                      • JKSHJ Offline
                        JKSHJ Offline
                        JKSH
                        Moderators
                        wrote on last edited by
                        #12

                        [P.S. This thread has been split -- the other part about stopping timers is now at https://forum.qt.io/topic/126168/global-static-qpixmapcache-in-qt-internals ]

                        Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                        1 Reply Last reply
                        2
                        • S Offline
                          S Offline
                          Suthiro
                          wrote on last edited by
                          #13

                          @kshegunov

                          I am now very confused. How and why QMetaObject::invokeMethod works, what it calls and what it fixes ...?

                          Please, see what @JKSH wrote in #2 above.

                          You receive a SIGSEGV, which is in a thread that you don't own, it's a system one from the kernel. This means nothing.

                          That is exactly why I have not posted it initially.

                          Which is also why QApplication instances should not be created in a library. Please describe what you're doing, and why! I admit we got carried away tracing the technical problem, but it does sound to me you're doing something very wrong ideologically.

                          I need a plugin (dll) with non-blocking main. An external third party program utilizes lua scripting, that's why I'm debugging it using "pure" lua.exe. The same crash occurs either using lua.exe and that program. If Qt's message loop is created in the function that being invoked by an external application, the application will wait for return of dllMain() forever and hang. That's why I need a Qt library with QApplication in a separate thread. I also could use QApplication in the main thread, but don't know how to handle the message loop properly in this situation. For the rest part of the library the location of QApplication does not matter, since I'm already using signals/slots & callbacks for communications between threads.

                          @JKSH

                          Please mark this thread as "Solved".

                          Okay, but will this thread stay open for further replies? Asking just in case and for further occasions. I have not tested with third-party program yet (takes too much time to prepare the data in the program), but based on my previous experience it is identical to pure lua.

                          The only side-effect I can think of is that you need to maintain a few more lines of code.
                          My code simply calls deleteLater() on every top-level widget in your program (this automatically deletes child widgets too), before quitting the QApplication.

                          This is really great!

                          Please help me test a simpler version (Comment out lines #1 and #2 below to trigger automatic quitting via quitOnLastWindowClosed) -- does this still work?:

                          It does! Thanks again.

                          Oh, please don't call setParent() on a widget that is not created with new. It can cause a different type of crash because the widget might get deleted twice.

                          No no, it was a just a test case! Never doing that in real life :)

                          JKSHJ 1 Reply Last reply
                          0
                          • S Suthiro

                            @kshegunov

                            I am now very confused. How and why QMetaObject::invokeMethod works, what it calls and what it fixes ...?

                            Please, see what @JKSH wrote in #2 above.

                            You receive a SIGSEGV, which is in a thread that you don't own, it's a system one from the kernel. This means nothing.

                            That is exactly why I have not posted it initially.

                            Which is also why QApplication instances should not be created in a library. Please describe what you're doing, and why! I admit we got carried away tracing the technical problem, but it does sound to me you're doing something very wrong ideologically.

                            I need a plugin (dll) with non-blocking main. An external third party program utilizes lua scripting, that's why I'm debugging it using "pure" lua.exe. The same crash occurs either using lua.exe and that program. If Qt's message loop is created in the function that being invoked by an external application, the application will wait for return of dllMain() forever and hang. That's why I need a Qt library with QApplication in a separate thread. I also could use QApplication in the main thread, but don't know how to handle the message loop properly in this situation. For the rest part of the library the location of QApplication does not matter, since I'm already using signals/slots & callbacks for communications between threads.

                            @JKSH

                            Please mark this thread as "Solved".

                            Okay, but will this thread stay open for further replies? Asking just in case and for further occasions. I have not tested with third-party program yet (takes too much time to prepare the data in the program), but based on my previous experience it is identical to pure lua.

                            The only side-effect I can think of is that you need to maintain a few more lines of code.
                            My code simply calls deleteLater() on every top-level widget in your program (this automatically deletes child widgets too), before quitting the QApplication.

                            This is really great!

                            Please help me test a simpler version (Comment out lines #1 and #2 below to trigger automatic quitting via quitOnLastWindowClosed) -- does this still work?:

                            It does! Thanks again.

                            Oh, please don't call setParent() on a widget that is not created with new. It can cause a different type of crash because the widget might get deleted twice.

                            No no, it was a just a test case! Never doing that in real life :)

                            JKSHJ Offline
                            JKSHJ Offline
                            JKSH
                            Moderators
                            wrote on last edited by JKSH
                            #14

                            @Suthiro said in QApplication in std::thread:

                            Please mark this thread as "Solved".

                            Okay, but will this thread stay open for further replies?

                            Yes, we can continue to reply after the thread is marked "Solved".

                            Asking just in case and for further occasions. I have not tested with third-party program yet (takes too much time to prepare the data in the program), but based on my previous experience it is identical to pure lua.

                            If you encounter further issues, it is best to post in a new thread anyway, since it is probably more complex than the example you posted in this thread.

                            Please help me test a simpler version (Comment out lines #1 and #2 below to trigger automatic quitting via quitOnLastWindowClosed) -- does this still work?:

                            It does! Thanks again.

                            No problem. Now, here's an even better way that doesn't require QMetaObject::invokeMethod() (thanks to @kshegunov). Does it still work if you remove QMetaObject::invokeMethod() and connect deleteLater() to aboutToQuit()?

                            QApplication a(argc, &argv);
                            ...
                            m_qPlotsControl = new QPlotsControl(...);
                            m_qPlotsControl->show();
                            
                            QObject::connect(&a, &QApplication::aboutToQuit, m_qPlotsControl, &QWidget::deleteLater);
                            
                            a.exec();
                            

                            No no, it was a just a test case! Never doing that in real life :)

                            Good to know :-D

                            Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                            1 Reply Last reply
                            0
                            • S Offline
                              S Offline
                              Suthiro
                              wrote on last edited by
                              #15

                              @JKSH

                              QObject::connect(&a, &QApplication::aboutToQuit, m_qPlotsControl, &QWidget::deleteLater);

                              Nope, it crashes. I think that aboutToQuit() is executed too late, probably in the same time when "ordinary" destructor would have been called.

                              kshegunovK 1 Reply Last reply
                              0
                              • S Suthiro

                                @JKSH

                                QObject::connect(&a, &QApplication::aboutToQuit, m_qPlotsControl, &QWidget::deleteLater);

                                Nope, it crashes. I think that aboutToQuit() is executed too late, probably in the same time when "ordinary" destructor would have been called.

                                kshegunovK Offline
                                kshegunovK Offline
                                kshegunov
                                Moderators
                                wrote on last edited by
                                #16

                                @Suthiro said in QApplication in std::thread:

                                Nope, it crashes. I think that aboutToQuit() is executed too late, probably in the same time when "ordinary" destructor would have been called.

                                It is not. It's called before QCoreApplication::exec returns, so I continue to suspect you have an unrelated crash, for which I reiterate that you should provide a meaningful stack trace.

                                Read and abide by the Qt Code of Conduct

                                1 Reply Last reply
                                0
                                • S Suthiro

                                  Thank to you all very much for your help and time! Since there are 22 (!) replies I'm sorry in advance if I missed something I need to reply to.

                                  @JKSH

                                  Quick and dirty hack: Comment out delete m_qPlotsControl; -- Does this stop the crash?

                                  I tried to delete, not to delete, to allocate everything (QApplication, QPushButton, QPlotsControl) on heap and on stack in all possible combinations. Crash persists.

                                  Ignore the message for now and see if you can get a stack trace for the crash. I haven't seen "pointers to a deep space" before -- Can you post the exact error messages?

                                  "An exception at address 0x000007FED396B168 raised in lua.exe: 0xC0000005: access rights violation during execution at 0x000007FED396B168." (original in Russian :"Вызвано исключение по адресу 0x000007FED396B168 в lua.exe: 0xC0000005: нарушение прав доступа при исполнении по адресу 0x000007FED396B168.")

                                  Stack trace:

                                  000007fed396b168()
                                  kernel32.dll!000000007702556d()
                                  ntdll.dll!000000007718372d()
                                  

                                  Thread: Win64 (working thread). It is neither the thread where QApplication is created, nor the "main" thread of the dll.
                                  Addresses, of course, change from run to run.

                                  Crash will not occur if the button was not interacted. A QTabWidget with custom widgets and buttons and whatever is interactable without problems.
                                  About "pointers to a deep space": I meant dangling pointers, sorry for not being serious & clear.

                                  // IMPORTANT: Make sure that all widgets are heap-allocated, not stack-allocated.
                                  // Call this from your main thread when you're ready to quit.

                                  the message about timers is issued, but no crash occurs. It is really great!

                                  Also, it looks like that only top-level widget must be heap allocated. QPushButton could be a stack-allocated member variable.
                                  ctor contains now exactly one line aside the initializers:

                                  QPushButton QPlotsControl::m_Btn;
                                  QPlotsControl::QPlotsControl(const WorkerThreadCallbackFn& cbFunc, const readyCallbackFn& readyCb,
                                  	QWidget *parent) : m_Callback{ cbFunc }, m_readyCallback { readyCb },
                                  	m_needsClosing{false},
                                  	QMainWindow(parent) { m_Btn.setParent(this); }
                                  

                                  Again, no crash if QMetaObject::invokeMethod is used. I think that children are destroyed properly.

                                  Important detail: If we don't interact with the QPushButton, then we don't get the message about timers.

                                  And no crashes!

                                  I believe it is fixable with m_qPlotsControl->deleteLater();... but that needs to be done before QApplication::exec() returns, because QDeferredDeleteEvent won't be processed without an event loop.

                                  It is fixable, thank you!

                                  Since the widget creates a timer in the background, then it's probably receiving background events/signals. delete could cause the event/signal handler to act on a dangling pointer -- that's how it could generate a crash. And that's why deleteLater() can fix the crash. (Alternatively, we could change from a heap-allocated widget to a stack-allocated widget)

                                  Stack allocated widget (actually, even all of them) is not enough. Just re-checked. I do really need to call that QMetaObject::invokeMethod to close the QApplication without crash.

                                  @kshegunov

                                  did you follow the threads?

                                  Yes, I did. The link that you've provided points to the code which is very similiar if not identical to that I use: create QApplication in a std::thread.

                                  Which if you notice passes the argc and argv along from main() to the thread. You should do that too.

                                  There is no argc/argv in a library.

                                  Can you give me a stack trace from that message?

                                  Please, see above.

                                  @Chris-Kawa

                                  I would consider this a Qt bug and expect it to crash all over the place.

                                  @kshegunov

                                  Yes, this is constructed appropriately, however the deallocation should've been tied to the QCoreApplication::aboutToQuit. I doubt anyone is going to bother fixing it though, I'm not sure if it's not going to be discarded with "this is unsupported" even ...

                                  @JKSH

                                  Me too, which is what I wrote somewhere upstairs - this should be fixed at the vendor side.

                                  As I understand, this is a bug in Qt after all. A workaround is to use QMetaObject::invokeMethod described by @JKSH. But are there any side effects?

                                  Thank you all once again!

                                  JKSHJ Offline
                                  JKSHJ Offline
                                  JKSH
                                  Moderators
                                  wrote on last edited by
                                  #17

                                  @Suthiro said in QApplication in std::thread:

                                  Nope, it crashes.

                                  Hmm, interesting.

                                  Stack trace:

                                  000007fed396b168()
                                  kernel32.dll!000000007702556d()
                                  ntdll.dll!000000007718372d()
                                  

                                  Can you please try to get a stack trace again, with a Debug build of your library (with a Debug build of Qt) and with QObject::connect(&a, &QApplication::aboutToQuit, m_qPlotsControl, &QWidget::deleteLater); ?

                                  Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                                  1 Reply Last reply
                                  0
                                  • S Offline
                                    S Offline
                                    Suthiro
                                    wrote on last edited by
                                    #18

                                    @kshegunov

                                    It is not. It's called before QCoreApplication::exec returns, so I continue to suspect you have an unrelated crash, for which I reiterate that you should provide a meaningful stack trace.

                                    @JKSH

                                    Can you please try to get a stack trace again, with a Debug build of your library (with a Debug build of Qt) and with QObject::connect(&a, &QApplication::aboutToQuit, m_qPlotsControl, &QWidget::deleteLater); ?

                                    could you please guide me how exactly should I get meaningful stack trace? I'm already using the debug version of Qt.

                                    I'm trying to isolate the crash, but looks like I need to start a fresh project. It could take some time.

                                    1 Reply Last reply
                                    0
                                    • S Offline
                                      S Offline
                                      Suthiro
                                      wrote on last edited by
                                      #19

                                      Okay, I figured it out how to reproduce the crash without lua.exe. The true culprit is static linking with Qt libraries: only if Qt was compiled with -static switch crash will occur.

                                      So, to reproduce:

                                      1. Build Qt with -static switch. To be exact, I used
                                        -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtquickcontrols -skip qtquickcontrols2 -skip qtquicktimeline -skip qtremoteobjects -skip qtscript -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qttranslations -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebglplugin -skip qtwebsockets -skip qtwebview -skip qtx11extras -no-appstore-compliant -no-sql-sqlite -no-sql-sqlite2 -no-sql-psql -no-sql-mysql -no-sql-odbc -no-sql-oci -no-sql-ibase -no-sql-db2 -no-sql-tds -no-dbus -no-icu -debug-and-release -nomake examples -nomake tests -opensource -confirm-license -mp -no-feature-sql -no-feature-testlib -static -prefix C:\temp\qt-test\qt5

                                      2. Build a dynamically linked library using QtPlotter.h and QtPlotter.cpp.

                                      3. Build an application using main.cpp. Please note that if the library is linked with the application at compile-time (regardless statically or dynamically), crash does not occur.

                                      4. Launch the application and interact with the button.
                                        5a. Close the window and enter any symbol except 's' -> crash occurs.
                                        5b. Do not close the window but enter 's' -> no crash.

                                      Application
                                      main.cpp

                                      #include <Windows.h>
                                      #include <iostream>
                                      #include "QtPlotter.h"
                                      
                                      int main()
                                      {
                                      	HMODULE qtlib = LoadLibrary(L"QtWidgetsApplication2.dll");
                                      	if (nullptr == qtlib) 
                                      		return EXIT_FAILURE;
                                      	auto start = (void(*)())GetProcAddress(qtlib, "Start");
                                      	if (nullptr != start)
                                      		start();
                                      	else
                                      		return EXIT_FAILURE;
                                      		
                                      	char ch=0;
                                      	std::cout << "enter 's' to exit correctly" << std::endl;
                                      	std::cin >> ch;
                                      	if (ch == 's')
                                      	{
                                      		auto stop = (void(*)())GetProcAddress(qtlib, "Stop");
                                      		if (nullptr != stop)
                                      			stop();
                                      		else
                                      			return EXIT_FAILURE;
                                      	}
                                      	if (nullptr != qtlib)
                                      		FreeLibrary(qtlib);
                                      	else
                                      		return EXIT_FAILURE;
                                      }
                                      

                                      Library
                                      QtPlotter.h

                                      #pragma once
                                      #include <memory>
                                      #include <thread>
                                      
                                      #ifdef BuildDll
                                      #include <QMainWindow>
                                      #include <QPushButton>
                                      class QPlotsControl : public QMainWindow
                                      {
                                      	Q_OBJECT
                                      
                                      public:
                                      	QPlotsControl(QWidget *parent = nullptr) : QMainWindow(parent)
                                      	{m_Btn = new QPushButton(this);}
                                      private:
                                      	QPushButton* m_Btn;
                                      };
                                      
                                      class QtPlotter
                                      {
                                      public:
                                      	QtPlotter();
                                      	~QtPlotter();
                                      	void Close();
                                      private:
                                      	void ApplicationLoop();
                                      	std::unique_ptr<std::thread> m_guiThread;
                                      };
                                      #endif
                                      
                                      #ifdef BuildDll
                                      #define DllImportExport extern "C" __declspec( dllexport )
                                      #else
                                      #define DllImportExport extern "C" __declspec( dllimport )
                                      #endif
                                      
                                      DllImportExport void Start();
                                      DllImportExport void Stop();
                                      

                                      QtPlotter.cpp

                                      #include "QtPlotter.h"
                                      #include <QApplication>
                                      
                                      QtPlotter* qtPlotter = nullptr;
                                      
                                      void QtPlotter::ApplicationLoop()
                                      {
                                      	char* argv = new char[4]{ '\0' };
                                      	auto val = "gui";
                                      	strcpy_s(argv, 4, val);
                                      	int argc = 1;
                                      	QApplication a(argc, &argv);
                                      	auto m_qPlotsControl = new QPlotsControl();
                                      	m_qPlotsControl->show();
                                      	//QObject::connect(&a, &QApplication::aboutToQuit, m_qPlotsControl, &QWidget::deleteLater);
                                      	a.exec();
                                      	delete[] argv;
                                      	return;
                                      }
                                      
                                      QtPlotter::QtPlotter()
                                      {
                                      	m_guiThread.reset(new std::thread([this] { this->ApplicationLoop(); }));
                                      }
                                      
                                      QtPlotter::~QtPlotter()
                                      {
                                      	m_guiThread->join();
                                      }
                                      
                                      void QtPlotter::Close()
                                      {
                                      	QMetaObject::invokeMethod(qApp, []
                                      	{
                                      		for (auto w : qApp->topLevelWidgets())
                                      			w->deleteLater();
                                      
                                      	}, Qt::BlockingQueuedConnection);
                                      }
                                      
                                      void Start()
                                      {
                                      	if (nullptr == qtPlotter)
                                      		qtPlotter = new QtPlotter();
                                      }
                                      
                                      void Stop()
                                      {
                                      	if (nullptr != qtPlotter)
                                      	{
                                      		qtPlotter->Close();
                                      		delete qtPlotter;
                                      		qtPlotter = nullptr;
                                      	}
                                      }
                                      
                                      JKSHJ 1 Reply Last reply
                                      0
                                      • S Suthiro

                                        Okay, I figured it out how to reproduce the crash without lua.exe. The true culprit is static linking with Qt libraries: only if Qt was compiled with -static switch crash will occur.

                                        So, to reproduce:

                                        1. Build Qt with -static switch. To be exact, I used
                                          -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtquickcontrols -skip qtquickcontrols2 -skip qtquicktimeline -skip qtremoteobjects -skip qtscript -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qttranslations -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebglplugin -skip qtwebsockets -skip qtwebview -skip qtx11extras -no-appstore-compliant -no-sql-sqlite -no-sql-sqlite2 -no-sql-psql -no-sql-mysql -no-sql-odbc -no-sql-oci -no-sql-ibase -no-sql-db2 -no-sql-tds -no-dbus -no-icu -debug-and-release -nomake examples -nomake tests -opensource -confirm-license -mp -no-feature-sql -no-feature-testlib -static -prefix C:\temp\qt-test\qt5

                                        2. Build a dynamically linked library using QtPlotter.h and QtPlotter.cpp.

                                        3. Build an application using main.cpp. Please note that if the library is linked with the application at compile-time (regardless statically or dynamically), crash does not occur.

                                        4. Launch the application and interact with the button.
                                          5a. Close the window and enter any symbol except 's' -> crash occurs.
                                          5b. Do not close the window but enter 's' -> no crash.

                                        Application
                                        main.cpp

                                        #include <Windows.h>
                                        #include <iostream>
                                        #include "QtPlotter.h"
                                        
                                        int main()
                                        {
                                        	HMODULE qtlib = LoadLibrary(L"QtWidgetsApplication2.dll");
                                        	if (nullptr == qtlib) 
                                        		return EXIT_FAILURE;
                                        	auto start = (void(*)())GetProcAddress(qtlib, "Start");
                                        	if (nullptr != start)
                                        		start();
                                        	else
                                        		return EXIT_FAILURE;
                                        		
                                        	char ch=0;
                                        	std::cout << "enter 's' to exit correctly" << std::endl;
                                        	std::cin >> ch;
                                        	if (ch == 's')
                                        	{
                                        		auto stop = (void(*)())GetProcAddress(qtlib, "Stop");
                                        		if (nullptr != stop)
                                        			stop();
                                        		else
                                        			return EXIT_FAILURE;
                                        	}
                                        	if (nullptr != qtlib)
                                        		FreeLibrary(qtlib);
                                        	else
                                        		return EXIT_FAILURE;
                                        }
                                        

                                        Library
                                        QtPlotter.h

                                        #pragma once
                                        #include <memory>
                                        #include <thread>
                                        
                                        #ifdef BuildDll
                                        #include <QMainWindow>
                                        #include <QPushButton>
                                        class QPlotsControl : public QMainWindow
                                        {
                                        	Q_OBJECT
                                        
                                        public:
                                        	QPlotsControl(QWidget *parent = nullptr) : QMainWindow(parent)
                                        	{m_Btn = new QPushButton(this);}
                                        private:
                                        	QPushButton* m_Btn;
                                        };
                                        
                                        class QtPlotter
                                        {
                                        public:
                                        	QtPlotter();
                                        	~QtPlotter();
                                        	void Close();
                                        private:
                                        	void ApplicationLoop();
                                        	std::unique_ptr<std::thread> m_guiThread;
                                        };
                                        #endif
                                        
                                        #ifdef BuildDll
                                        #define DllImportExport extern "C" __declspec( dllexport )
                                        #else
                                        #define DllImportExport extern "C" __declspec( dllimport )
                                        #endif
                                        
                                        DllImportExport void Start();
                                        DllImportExport void Stop();
                                        

                                        QtPlotter.cpp

                                        #include "QtPlotter.h"
                                        #include <QApplication>
                                        
                                        QtPlotter* qtPlotter = nullptr;
                                        
                                        void QtPlotter::ApplicationLoop()
                                        {
                                        	char* argv = new char[4]{ '\0' };
                                        	auto val = "gui";
                                        	strcpy_s(argv, 4, val);
                                        	int argc = 1;
                                        	QApplication a(argc, &argv);
                                        	auto m_qPlotsControl = new QPlotsControl();
                                        	m_qPlotsControl->show();
                                        	//QObject::connect(&a, &QApplication::aboutToQuit, m_qPlotsControl, &QWidget::deleteLater);
                                        	a.exec();
                                        	delete[] argv;
                                        	return;
                                        }
                                        
                                        QtPlotter::QtPlotter()
                                        {
                                        	m_guiThread.reset(new std::thread([this] { this->ApplicationLoop(); }));
                                        }
                                        
                                        QtPlotter::~QtPlotter()
                                        {
                                        	m_guiThread->join();
                                        }
                                        
                                        void QtPlotter::Close()
                                        {
                                        	QMetaObject::invokeMethod(qApp, []
                                        	{
                                        		for (auto w : qApp->topLevelWidgets())
                                        			w->deleteLater();
                                        
                                        	}, Qt::BlockingQueuedConnection);
                                        }
                                        
                                        void Start()
                                        {
                                        	if (nullptr == qtPlotter)
                                        		qtPlotter = new QtPlotter();
                                        }
                                        
                                        void Stop()
                                        {
                                        	if (nullptr != qtPlotter)
                                        	{
                                        		qtPlotter->Close();
                                        		delete qtPlotter;
                                        		qtPlotter = nullptr;
                                        	}
                                        }
                                        
                                        JKSHJ Offline
                                        JKSHJ Offline
                                        JKSH
                                        Moderators
                                        wrote on last edited by
                                        #20

                                        @Suthiro said in QApplication in std::thread:

                                        Okay, I figured it out how to reproduce the crash without lua.exe. The true culprit is static linking with Qt libraries: only if Qt was compiled with -static switch crash will occur.

                                        ...

                                        Please note that if the library is linked with the application at compile-time (regardless statically or dynamically), crash does not occur.

                                        Thanks again for your detailed tests and reports.

                                        Unfortunately, static libraries and LoadLibrary() are outside my expertise; I can't think of a reason why static Qt or LoadLibrary() would produce different behaviour compared to dynamic Qt or compile-time linking.

                                        By the way, what license are you using Qt under? I you want to use LGPLv3, use statically-linked Qt, AND keep your software closed-source, you'll need to take extra steps to allow your users to switch the version of Qt. (No extra steps needed if your software is open-source)

                                        I use MSVC 2017 (toolset v141) on WIndows 7 x64. Compiled qt5.14.1 from source, but also tried some pre-built binaries.

                                        Hmm... the official pre-built binaries are dynamic only. Just to confirm: Does this mean that dynamic Qt crashes from Lua but doesn't crash from your test app?

                                        could you please guide me how exactly should I get meaningful stack trace? I'm already using the debug version of Qt.

                                        ...

                                        1. Build Qt with -static switch. To be exact, I used...
                                        1. Add the -developer-build switch when building Qt.
                                          • For some types of debugging, the regular Debug build is detailed enough.
                                          • The Developer build exports even more debugging symbols from Qt compared to the regular Debug build -- see https://wiki.qt.io/Building_Qt_5_from_Git
                                        2. Build QtPlotter and link it to the developer build of Qt.
                                        3. Build your test app.
                                        4. Use a Debugger to launch the test app (or your Lua app)
                                          • I've found that WinDbg provides more details than GDB
                                        5. Let your app crash and obtain a detailed stack trace from the debugger
                                          • If using WinDbg, press 'k' to get the stack trace after the crash

                                        Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                                        kshegunovK 1 Reply Last reply
                                        0
                                        • S Offline
                                          S Offline
                                          Suthiro
                                          wrote on last edited by
                                          #21

                                          @JKSH

                                          Thanks again for your detailed tests and reports.
                                          Unfortunately, static libraries and LoadLibrary() are outside my expertise; I can't think of a reason why static Qt or LoadLibrary() would produce different behaviour compared to dynamic Qt or compile-time linking.

                                          I don't know either. I just started from scratch and found what is different. It is very strange indeed.

                                          By the way, what license are you using Qt under? I you want to use LGPLv3, use statically-linked Qt, AND keep your software closed-source, you'll need to take extra steps to allow your users to switch the version of Qt. (No extra steps needed if your software is open-source)

                                          It is a private project (hobby-related), so I'm okay with any license that allows me to use Qt for personal purposes :). In case I decide to make it public for some reason (unlikely), it will be definitely open-source. TBH I don't need static linking with Qt, it is matter of habit to keep things tidier. So I'll just switch to dynamic linking.

                                          I use MSVC 2017 (toolset v141) on WIndows 7 x64. Compiled qt5.14.1 from source, but also tried some pre-built binaries.
                                          

                                          Hmm... the official pre-built binaries are dynamic only. Just to confirm: Does this mean that dynamic Qt crashes from Lua but doesn't crash from your test app?

                                          I tried to reproduce the behaviour with pre-built binaries to no avail, I believe there are/were more than one reason to crash in the "big" project. I was focused at the "QTimer" message and QPushButton interaction troubles, so it is very likely that I broke something else. But now I can get rid at least of this one and proceed further. The example I provided crashes both using lua and test app given that Qt was linked statically.

                                          Add the -developer-build switch when building Qt.
                                          For some types of debugging, the regular Debug build is detailed enough.
                                          The Developer build exports even more debugging symbols from Qt compared to the regular Debug build -- see https://wiki.qt.io/Building_Qt_5_from_Git
                                          Build QtPlotter and link it to the developer build of Qt.
                                          Build your test app.
                                          Use a Debugger to launch the test app (or your Lua app)
                                          I've found that WinDbg provides more details than GDB
                                          Let your app crash and obtain a detailed stack trace from the debugger
                                          If using WinDbg, press 'k' to get the stack trace after the crash

                                          okay, I'll build with -developer-build and report later.

                                          Thank you very much for your help!

                                          1 Reply Last reply
                                          0

                                          • Login

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved