Results 1 to 8 of 8

Thread: signal/slots across threads in Qt4

  1. #1
    Join Date
    Jul 2006
    Posts
    3
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Windows

    Default signal/slots across threads in Qt4

    Hello,

    Qt4 claims to have a thread safe way of emitting signals between threads. Many of Qt4's slots have Qt4 data types in their signature. The copy constructors of these types are reentrant but not thread safe. However this problem is avoided since most of the time const references are used as slot arguments e.g. void mySlot(QString const& arg). That means that the slot gets the address of the data but cannot modify these data. Okay! But what about the thread that emitted the signal. I see trouble when in the emitting thread the QString goes out of scope or is modified before the slot dealt with the QString. What stunned me was that my program nevertheless crashed when I used in the emitting thread a const QString that did not went out of scope!! When I wrote similar code for a slot with an integer argument the program did not crash. My conclusions: 1. Qt4 is doing something dirty when queing a signal to the receivers event loop. 2. Qt4's signal/slot mechanism across threads is NOT THREAD SAFE.

    I include my code which I have build using the Visual Studio .NET 2003 compiler on a Windows XP-system.
    Can anybody either confirm my observations/conclusions or state whether I did some wrong coding?
    Thanks in advance!

    Qt Code:
    1. // FILE 1: receiver.h
    2. #ifndef __RECEIVER__H__
    3. #define __RECEIVER__H__
    4. #include <QtCore>
    5.  
    6.  
    7.  
    8. class Receiver : public QObject
    9. {
    10. Q_OBJECT
    11. public:
    12. Receiver(QObject *parent = 0) : QObject(parent)
    13. {
    14. }
    15. public slots:
    16. void mySlot(QString const& paS);
    17. void myIntegerSlot(int paI);
    18. };
    19.  
    20.  
    21.  
    22.  
    23.  
    24. #endif
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. // FILE 2 emitter.h
    2. #ifndef __EMITTER__H__
    3. #define __EMITTER__H__
    4. #include <QtCore>
    5.  
    6. class Receiver;
    7.  
    8.  
    9.  
    10. class Emitter : public QObject
    11. {
    12. Q_OBJECT
    13. public:
    14. Emitter(QObject *parent = 0) : QObject(parent)
    15. {
    16. }
    17. void emitSignal(QString const& paS);
    18. void emitIntegerSignal(int paI);
    19. signals:
    20. void newString(QString const& paS);
    21. void newInteger(int paI);
    22. };
    23.  
    24.  
    25.  
    26. class EmitterThread : public QThread
    27. {
    28. public:
    29. EmitterThread(Receiver *receiver);
    30. void run();
    31. private:
    32. Receiver *meReceiver;
    33. Emitter *meEmitter;
    34. };
    35.  
    36.  
    37. #endif
    To copy to clipboard, switch view to plain text mode 


    Qt Code:
    1. // FILE 3 receiver.cpp
    2. #include "receiver.h"
    3. #include <iostream>
    4. using namespace std;
    5.  
    6. void Receiver::mySlot(QString const& paS)
    7. {
    8. cout << paS.toStdString() << endl;
    9. }
    10.  
    11.  
    12. void Receiver::myIntegerSlot(int paI)
    13. {
    14. cout << paI << endl;
    15. }
    To copy to clipboard, switch view to plain text mode 


    Qt Code:
    1. // FILE 4: emitter.cpp
    2. #include "emitter.h"
    3. #include "receiver.h"
    4.  
    5.  
    6. #include <iostream>
    7. using namespace std;
    8.  
    9. void Emitter::emitSignal(QString const& paS)
    10. {
    11. emit newString(paS);
    12. }
    13.  
    14.  
    15. void Emitter::emitIntegerSignal(int paI)
    16. {
    17. emit newInteger(paI);
    18. }
    19.  
    20.  
    21.  
    22.  
    23. EmitterThread::EmitterThread(Receiver *receiver) : QThread(0), meReceiver(receiver)
    24. {
    25. meEmitter = new Emitter;
    26. QObject::connect(meEmitter,SIGNAL(newString(QString const&)),
    27. meReceiver,SLOT(mySlot(QString const&)),
    28. Qt::QueuedConnection);
    29. QObject::connect(meEmitter,SIGNAL(newInteger(int)),
    30. meReceiver,SLOT(myIntegerSlot(int)),
    31. Qt::QueuedConnection);
    32.  
    33. }
    34.  
    35. /* This run method crashes
    36. void EmitterThread::run()
    37. {
    38.   QString loText("");
    39.   while (true)
    40.   {
    41.   QThread::sleep(1);
    42.   meEmitter->emitSignal(loText);
    43.   loText += QString("_");
    44.   }
    45. }
    46. */
    47.  
    48.  
    49. /* This run method crashes
    50. void EmitterThread::run()
    51. {
    52.   while (true)
    53.   {
    54.   this->sleep(1);
    55.   meEmitter->emitSignal(QString("I am not thread safe"));
    56.   }
    57. }
    58. */
    59.  
    60. /* This run method crashes
    61. void EmitterThread::run()
    62. {
    63.   while (true)
    64.   {
    65.   QThread::sleep(1);
    66.   {
    67.   QString loText("I go out of scope while my colleague thread is using me!");
    68.   meEmitter->emitSignal(loText);
    69.   }
    70.   }
    71. }
    72. */
    73.  
    74. // This run method crashes
    75. void EmitterThread::run()
    76. {
    77. QString const loText("I go not out of scope and am not modified");
    78. while (true)
    79. {
    80. this->sleep(1);
    81. meEmitter->emitSignal(loText);
    82. }
    83. }
    84.  
    85.  
    86.  
    87.  
    88. /* This run method does not crash (it emits a copy of an integer)
    89. void EmitterThread::run()
    90. {
    91.   int i = 0;
    92.   while (true)
    93.   {
    94.   this->msleep(10);
    95.   meEmitter->emitIntegerSignal(i);
    96.   i++;
    97.   }
    98. }
    99. */
    To copy to clipboard, switch view to plain text mode 



    Qt Code:
    1. // FILE 5: main.cpp
    2. #include "receiver.h"
    3. #include "emitter.h"
    4.  
    5. #include <QtCore>
    6. #include <QtGui>
    7.  
    8. int main(int argc, char *argv[])
    9. {
    10. QApplication app(argc,argv);
    11. Receiver loReceiver; // lives in Main Thread
    12.  
    13. EmitterThread loET(&loReceiver);
    14. loET.start();
    15. return app.exec();
    16. }
    To copy to clipboard, switch view to plain text mode 
    Last edited by jacek; 28th July 2006 at 10:19. Reason: added code tags

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    5,372
    Thanks
    28
    Thanked 976 Times in 912 Posts
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11 Windows

    Default Re: signal/slots across threads in Qt4

    Quote Originally Posted by Ambiorix
    I see trouble when in the emitting thread the QString goes out of scope or is modified before the slot dealt with the QString.
    There won't be any trouble, because in case of queued connections Qt makes a copy of all parameters.

    Quote Originally Posted by Ambiorix
    Can anybody either confirm my observations/conclusions
    I tried all "crashing" versions of run() method and they didn't crash on my system (PLD Linux, Qt 4.1.4, g++ 3.3.6). How often does it crash on your system?

  3. #3
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: signal/slots across threads in Qt4

    Quote Originally Posted by Ambiorix
    Hello,
    Hi

    Qt4 claims to have a thread safe way of emitting signals between threads. Many of Qt4's slots have Qt4 data types in their signature. The copy constructors of these types are reentrant but not thread safe. However this problem is avoided since most of the time const references are used as slot arguments e.g. void mySlot(QString const& arg). That means that the slot gets the address of the data but cannot modify these data. Okay! But what about the thread that emitted the signal. I see trouble when in the emitting thread the QString goes out of scope or is modified before the slot dealt with the QString.
    No, that's not true. Queued connections perform a serialisation and deserialisation of objects passed as signal arguments, so it doesn't matter if the object goes out of scope before the slot is executed or not. You can see that if you create some custom object and try to use it as a signal argument or if you try to use a non-const reference to some object. Result:
    "QObject::connect: Cannot queue arguments of type 'QString&'"

    What stunned me was that my program nevertheless crashed when I used in the emitting thread a const QString that did not went out of scope!! When I wrote similar code for a slot with an integer argument the program did not crash. My conclusions: 1. Qt4 is doing something dirty when queing a signal to the receivers event loop. 2. Qt4's signal/slot mechanism across threads is NOT THREAD SAFE.
    My conclusion:
    1. Your conclusions seem not to be reliably confirmed, as your test program runs all the time while I am writing this post and it didn't crash. BTW. You had a spelling error in one of the connect statements... Maybe that was the problem?

    I include my code which I have build using the Visual Studio .NET 2003 compiler on a Windows XP-system.
    I tested it under Linux/GCC4 and it runs fine.

    Ok, I am killing the app now and trying some other version of "crashing" run()... Seems to work flawlessly...

  4. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    5,372
    Thanks
    28
    Thanked 976 Times in 912 Posts
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11 Windows

    Default Re: signal/slots across threads in Qt4

    Does this crash?
    Qt Code:
    1. EmitterThread::EmitterThread(Receiver *receiver) : QThread(0), meReceiver(receiver)
    2. {
    3. // empty
    4. }
    5.  
    6. void EmitterThread::run()
    7. {
    8. meEmitter = new Emitter;
    9. QObject::connect(meEmitter,SIGNAL(newString(QString const&)),
    10. meReceiver,SLOT(mySlot(QString const&)),
    11. Qt::QueuedConnection);
    12. QObject::connect(meEmitter,SIGNAL(newInteger(int)),
    13. meReceiver,SLOT(myIntegerSlot(int)),
    14. Qt::QueuedConnection);
    15. QString loText("");
    16. while (true)
    17. {
    18. QThread::sleep(1);
    19. meEmitter->emitSignal(loText);
    20. loText += QString("_");
    21. }
    22. }
    To copy to clipboard, switch view to plain text mode 

  5. The following user says thank you to jacek for this useful post:

    Ambiorix (28th July 2006)

  6. #5
    Join Date
    Jul 2006
    Posts
    3
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Windows

    Smile Re: signal/slots across threads in Qt4

    Hello Jacek & Wysota,

    Thank you for running my program and giving feedback about the copying/serialisation of the queued signal arguments.

    The code that you posted Jacek works fine and I believe to understand why. In my original code I created the emitter object in the constructor of the thread. So the emitter object was also living in the main thread. Thus I was using queued connections between objects in the same (main) thread. By moving the creation of the emitter object to the run method, the emitter lives now in the thread.

    Kind regards,

    Ambiorix

  7. #6
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    5,372
    Thanks
    28
    Thanked 976 Times in 912 Posts
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11 Windows

    Default Re: signal/slots across threads in Qt4

    Quote Originally Posted by Ambiorix
    So the emitter object was also living in the main thread. Thus I was using queued connections between objects in the same (main) thread. By moving the creation of the emitter object to the run method, the emitter lives now in the thread.
    Yes, many people run into this problem. I wonder why your version was crashing on windows, but not on Linux. I guess it must have something to do with a different way of handling thread data in those systems.

  8. #7
    Join Date
    Apr 2006
    Location
    San Francisco, CA
    Posts
    186
    Thanks
    55
    Thanked 12 Times in 11 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Windows

    Default Re: signal/slots across threads in Qt4

    I ran the original crashing code in VS2005 WinXP, but it doesn't crash on my system.
    Software Engineer



  9. #8
    Join Date
    Jul 2006
    Posts
    3
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: signal/slots across threads in Qt4

    Hello Gfunk,

    I cannot explain why the code crashes on my system but not on yours. Maybe it is in the compiler flags that I am using or in the Qt version. I include the build output so that you know which flags and versions I am using.

    The feedback that I get when the crash occurs is the following:
    QThread object destroyed while thread is still running.
    QMutex::lock(): Deadlock detected in thread 2660


    build:
    scons: Reading SConscript files ...
    Loading qt4 tool...
    scons: done reading SConscript files.
    scons: Building targets ...
    cl /nologo /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /EHsc /MT /TP -DQT_GUI_LIB /IC:\Qt\4.1.1\include /IC:\Qt\4.1.1\include\QtGui /IC:\Qt\4.1.1\include\QtCore /c emitter.cpp /Foemitter.obj
    emitter.cpp
    cl /nologo /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /EHsc /MT /TP -DQT_GUI_LIB /IC:\Qt\4.1.1\include /IC:\Qt\4.1.1\include\QtGui /IC:\Qt\4.1.1\include\QtCore /c main.cpp /Fomain.obj
    main.cpp
    C:\Qt\4.1.1\bin\moc -o moc_emitter.cpp emitter.h
    cl /nologo /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /EHsc /MT /TP -DQT_GUI_LIB /IC:\Qt\4.1.1\include /IC:\Qt\4.1.1\include\QtGui /IC:\Qt\4.1.1\include\QtCore /c moc_emitter.cpp /Fomoc_emitter.obj
    moc_emitter.cpp
    C:\Qt\4.1.1\bin\moc -o moc_receiver.cpp receiver.h
    cl /nologo /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /EHsc /MT /TP -DQT_GUI_LIB /IC:\Qt\4.1.1\include /IC:\Qt\4.1.1\include\QtGui /IC:\Qt\4.1.1\include\QtCore /c moc_receiver.cpp /Fomoc_receiver.obj
    moc_receiver.cpp
    cl /nologo /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /EHsc /MT /TP -DQT_GUI_LIB /IC:\Qt\4.1.1\include /IC:\Qt\4.1.1\include\QtGui /IC:\Qt\4.1.1\include\QtCore /c receiver.cpp /Foreceiver.obj
    receiver.cpp
    link /nologo /subsystem:console /OUT:safety.exe /LIBPATH:C:\Qt\4.1.1\lib qtgui4.lib qtcore4.lib receiver.obj emitter.obj main.obj moc_receiver.obj moc_emitter.obj
    scons: done building targets.

Similar Threads

  1. Threads in GUI application
    By blackliteon in forum Qt Programming
    Replies: 7
    Last Post: 14th April 2006, 09:48
  2. Threads communication
    By probine in forum Qt Programming
    Replies: 4
    Last Post: 31st March 2006, 14:46
  3. Replies: 16
    Last Post: 7th March 2006, 15:57
  4. QMessageBox and threads
    By vfernandez in forum Qt Programming
    Replies: 3
    Last Post: 5th February 2006, 17:24
  5. [QT4] threads, signals, and slots, please help.
    By ucntcme in forum Qt Programming
    Replies: 12
    Last Post: 25th January 2006, 14:23

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.