Results 1 to 3 of 3

Thread: Error when calling quit() on a QCoreApplication within a QThread

  1. #1
    Join Date
    Aug 2009
    Posts
    50
    Thanks
    9
    Qt products
    Qt4
    Platforms
    Windows

    Default Error when calling quit() on a QCoreApplication within a QThread

    In an effort to create a Qt event loop in a separate thread, from within a DLL which is called by a main application written in Java, I have done the following, based on a suggestion I read here at http://stackoverflow.com/questions/2...qt-application, which works rather well:

    Qt Code:
    1. // Define a global namespace. We need to do this because the parameters passed to QCoreApplication
    2. // must have a lifetime exceeding that of the QCoreApplication object
    3. namespace ToolThreadGlobal
    4. {
    5. static int argc = 1;
    6. static char * argv[] = { "MyVirtualMainApplication.exe", NULL };
    7. static QCoreApplication *coreApp = nullptr;
    8. static ToolThread *toolThread = nullptr;
    9. };
    10.  
    11. //! The ToolThread class differs from a standard QThread only in its run() method
    12. class ToolThread : public QThread
    13. {
    14. //! Override QThread's run() method so that it calls the QCoreApplication's exec() method
    15. //! rather than the QThread's own
    16. void run() { ToolThreadGlobal::coreApp -> exec(); }
    17. };
    18.  
    19. class ThreadStarter : public QObject
    20. {
    21. Q_OBJECT
    22.  
    23. public:
    24. //! Constructor
    25. ThreadStarter()
    26. {
    27. // Set up the tool thread:
    28. if (!ToolThreadGlobal::toolThread)
    29. {
    30. ToolThreadGlobal::toolThread = new ToolThread();
    31. connect(ToolThreadGlobal::toolThread, &ToolThread::started,
    32. this, &ThreadStarter::createApplication, Qt::DirectConnection);
    33. // The thread's 'started' event is emitted after the thread is started but
    34. // before its run() method is invoked. By arranging for the createApplication
    35. // subroutine to be called before run(), we ensure that the required QCoreApplication
    36. // object is instantiated *within the thread* before ToolThread's customised run()
    37. // method calls the application's exec() command.
    38. ToolThreadGlobal::toolThread->start();
    39. }
    40. }
    41.  
    42. //! Destructor
    43. ~ThreadStarter()
    44. {
    45. // Ensure that the thread and the QCoreApplication are cleanly shut down:
    46. ToolThreadGlobal::coreApp -> quit();
    47. delete ToolThreadGlobal::coreApp;
    48. ToolThreadGlobal::coreApp = nullptr;
    49. delete ToolThreadGlobal::toolThread;
    50. ToolThreadGlobal::toolThread = nullptr;
    51. }
    52.  
    53. //! Function to return a pointer to the actual tool thread:
    54. ToolThread* getThread() const { return ToolThreadGlobal::toolThread; }
    55.  
    56. private:
    57. //! Create the QCoreApplication that will provide the tool thread's event loop
    58. /*! This subroutine is intended to be called from the tool thread itself as soon
    59.   as the thread starts up.
    60.   */
    61. void createApplication()
    62. {
    63. // Start the QCoreApplication event loop, so long as no other Qt event loop
    64. // is already running
    65. if (QCoreApplication::instance() == NULL)
    66. {
    67. ToolThreadGlobal::coreApp = new QCoreApplication(ToolThreadGlobal::argc,
    68. ToolThreadGlobal::argv);
    69. }
    70. }
    71. };
    To copy to clipboard, switch view to plain text mode 

    To use this, a subroutine called from the main Java applications’ thread just needs to create a ThreadStarter object
    which will automatically create a ToolThread with a QCoreApplication running inside it:

    Qt Code:
    1. itsThreadStarter = new ThreadStarter();
    2. itsToolThread = itsThreadStarter -> getThread();
    To copy to clipboard, switch view to plain text mode 

    We can then instantiate a QObject class in the usual way, move it to the thread and call its methods asynchronously using QMetaObject::invokeMethod:

    Qt Code:
    1. itsWorker = new Worker();
    2. itsWorker -> moveToThread(itsToolThread);
    3.  
    4. QMetaObject::invokeMethod(itsWorker, “doSomethingInteresting”);
    To copy to clipboard, switch view to plain text mode 

    When we’re done, we just delete the ThreadStarter object and everything is cleaned up nicely. Apart from the annoying message saying

    WARNING: QApplication was not created in the main() thread

    on startup, it seems to meet all my requirements.

    Except… (and here, at last, is my question).

    Occasionally - and without any pattern that I’ve been able to discern so far - I get an error during the shutdown process. Usually it occurs at the line

    Qt Code:
    1. delete ToolThreadGlobal::coreApp;
    To copy to clipboard, switch view to plain text mode 

    but sometimes at the line

    Qt Code:
    1. ToolThreadGlobal::coreApp -> exec();
    To copy to clipboard, switch view to plain text mode 

    (which of course is executed in the thread’s run() method and doesn’t return until after ToolThreadGlobal::coreApp -> quit(); has been fully executed).

    Often the error message is a simple access violation; sometimes it’s a rather more involved:

    ASSERT failure in QObjectPrivate::deleteChildren(): "isDeletingChildren already set, did this function recurse?", file ..\qtbase\src\corelib\kernel\qobject.cpp, line 1927

    I assume it’s because, once I issue the quit() command to the QCoreApplication, I should be waiting for a little while for it to close down the event loop properly before deleting it - just as one would usually call quit() and then wait() on an ordinary QThread before deleting it. However, QCoreApplication doesn’t seem to have the equivalent of a wait() command, and I can’t implement a QTimer to force a delay because it wouldn’t work once I’ve closed down the event loop with quit(). I’m therefore at a loss what to do. I have an inkling that, as QCoreApplication is a QObject, I could call its deleteLater() method but I can’t see where I should call it from.

    Is there an expert out there who understands the ins and outs of QCoreApplication and QThread well enough to suggest a solution to this? Or is there a fundamental flaw in the way that I have designed this?

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,419
    Thanks
    37
    Thanked 1,546 Times in 1,496 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Error when calling quit() on a QCoreApplication within a QThread

    What if you release a static QSemaphore in the thread's run() method after exec() has returned and acquire() it from the line after calling quit()?

    Assuming the ThreadStarter destructor is called by a different thread of course.

    Cheers,
    _

  3. #3
    Join Date
    Aug 2009
    Posts
    50
    Thanks
    9
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: Error when calling quit() on a QCoreApplication within a QThread

    Thank you. I tried that but unfortunately it didn't work. I don't know why, as the logic of it seemed sound.

    Anyway, I've found something which (to the extent that I've tested it so far) does work. I add a static 'cleanup' function to my global namespace:

    Qt Code:
    1. namespace ToolThreadGlobal
    2. {
    3. static int argc = 1;
    4. static char * argv[] = { "MyVirtualMainApplication.exe", NULL };
    5. static QCoreApplication *coreApp = nullptr;
    6. static ToolThread *toolThread = nullptr;
    7. static void cleanup() { coreApp->deleteLater(); coreApp = nullptr; }
    8. };
    To copy to clipboard, switch view to plain text mode 

    Then, from my "createApplication" slot I connect the QCoreApplication's aboutToQuit signal to it:

    Qt Code:
    1. void createApplication()
    2. {
    3. // Start the QCoreApplication event loop, so long as no other Qt event loop
    4. // is already running
    5. if (QCoreApplication::instance() == NULL)
    6. {
    7. ToolThreadGlobal::coreApp = new QCoreApplication(ToolThreadGlobal::argc,
    8. ToolThreadGlobal::argv);
    9. connect(ToolThreadGlobal::coreApp, &QCoreApplication::aboutToQuit,
    10. ToolThreadGlobal::cleanup);
    11. }
    12. }
    To copy to clipboard, switch view to plain text mode 

    Then the 'ThreadStarter' destructor is reduced to just five lines (including the addition of calls to QThread::quit() and QThread::wait())

    Qt Code:
    1. ~ThreadStarter()
    2. {
    3. // Ensure that the thread and the QCoreApplication are cleanly shut down:
    4. ToolThreadGlobal::coreApp -> quit();
    5. ToolThreadGlobal::toolThread -> quit();
    6. ToolThreadGlobal::toolThread -> wait();
    7. delete ToolThreadGlobal::toolThread;
    8. ToolThreadGlobal::toolThread = nullptr;
    9. }
    To copy to clipboard, switch view to plain text mode 

    When the ThreadStarter destructor calls QCoreApplication::quit(), the QCoreApplication calls the cleanup function while its event loop is still running. This schedules the QCoreApplication to delete itself once it is good and ready, and in the meantime resets the global pointer to NULL so that the rest of the application knows that a new QCoreApplication can be instantiated when needed.

    I guess this leaves a very small risk that there could be a conflict if the main application immediately creates a new QCoreApplication and tries to run exec() on it while the old QCoreApplication is still in the process of cleaning itself up. I don't think that is likely to happen in the context where I'm using it. Even so, there may be a way of guarding against this by using QCoreApplication::instance(), but I really don't know how that would behave if the old instance is already on its way out.
    Last edited by Eos Pengwern; 23rd June 2014 at 14:48. Reason: updated contents

Similar Threads

  1. Replies: 3
    Last Post: 7th April 2011, 12:09
  2. Replies: 2
    Last Post: 1st December 2010, 12:33
  3. QThread quit() issue.
    By rokkamraja in forum Qt Programming
    Replies: 1
    Last Post: 15th December 2009, 17:34
  4. Quit, Exit qApp (program) if error?
    By Arsenic in forum Newbie
    Replies: 13
    Last Post: 30th September 2008, 13:59
  5. QThread exit()/quit() question
    By TheKedge in forum Qt Programming
    Replies: 1
    Last Post: 28th August 2006, 15:38

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.