Problem with QTimer --> get message "Object::startTimer: Timers cannot be started from another thread"



  • Hi,

    In my program, I have a MIDIControllerGenericDialogMOD1 class where I define a static variable to hold a pointer a BlownUpParamDisplay class (derived from) QDialog.
    I declare the pointer to BlownUpParamDisplay to be static since I only need one instance of that class

    static BlownUpParamDisplay *m_dlgBlownUpParamDisplay;
    

    and also declare a QTimer that will allow me to hide the m_dlgBlownUpParamDisplay window after 1.5 sec :

    QTimer m_timerDlgBlownUpParamDisplay;
    

    The link is made with :

    connect( &m_timerDlgBlownUpParamDisplay, SIGNAL(timeout()), this, SLOT(closeDlgBlownUpParamDisplay()) );
    

    and there is a start and a stop disaplay function

    void MIDIControllerGenericDialogMOD1::closeDlgBlownUpParamDisplay()
    {
        m_dlgBlownUpParamDisplay->hide();
        this->raise();
    }
    
    void MIDIControllerGenericDialogMOD1::startDlgBlownUpParamDisplayTimer()
    {
        m_timerDlgBlownUpParamDisplay.setSingleShot(true);
        m_timerDlgBlownUpParamDisplay.start(1500);
    }
    

    For the m_dlgBlownUpParamDisplay window :
    it is defined (in .h file) by :

    static BlownUpParamDisplay *m_dlgBlownUpParamDisplay;
    

    initialized (in .cpp file) by :

    BlownUpParamDisplay *MIDIControllerGenericDialogMOD1::m_dlgBlownUpParamDisplay = NULL;
    

    and created in the creator function of the by :

    if( m_dlgBlownUpParamDisplay == NULL) m_dlgBlownUpParamDisplay = new BlownUpParamDisplay;
    

    just before the Signal/Slot connection is made :

    connect( &m_timerDlgBlownUpParamDisplay, SIGNAL(timeout()), this, SLOT(closeDlgBlownUpParamDisplay()) );
    

    The PROBLEMS I get with that code are :

    1. When the startDlgBlownUpParamDisplayTimer is called in the program, I get the following message in the application output
      "Object::startTimer: Timers cannot be started from another thread"

    2. When the closeDlgBlownUpParamDisplay is called in the program, I get the following message in the application output
      "Object::killTimer: Timers cannot be stoped from another thread"

    3. The m_dlgBlownUpParamDisplay window is displayed (and updated with new info) but never closes (apart from manually). It's a shame because all that code is to make the windows automatically disappear after 1.5 sec.

    The program does not used QThread. only the RTMIDI lib used in the program has a C pthread for input MIDI messages. This display of the m_dlgBlownUpParamDisplay window happens after the MIDI callback function is used.

    I had this code working before but I was going through the application message queue after receiving a MIDI message, so I guess I was actually changing thread. I would like to avoid that because I don't want the message to go through a queue where there could be queued up after other messages and I need the MIDI message to be processed as soon as they are received. So I you have an idea on how to make that code work without going through the main application message queue please let me know.

    Thanks in advanCe for the help.



  • U must be creating qtimer in one thread and trying to start the timer in another thread. Just check ownership of qtimer object



  • Yes that is likely what is going on. --> I'll check ownership tomorrow .

    In the meantime I tried sending Start/stop event for the display to solve the problem. That way the MIDI message get processed immediatly and the refresh of the BlownUp screen get done potentially latter through the event queue :

    void MIDIControllerGenericDialogMOD1::customEvent ( QEvent *event )
    {
        //QString MIDI_Message = QString("Message Received" );
        //ui.logMIDI->append(MIDI_Message);
        //qDebug() << "customEvent:" << event->type();
    
        if( event->type() ==  BlownUpStartEventType) {
            startDlgBlownUpParamDisplayTimer();
        }
        if( event->type() ==  BlownUpStopEventType) {
            closeDlgBlownUpParamDisplay();
        }
    }
    

    the start event is send with :

                ev = new BlownUpStartEvent( );
                QApplication::postEvent(this, ev);
    

    and the stop event is triggered by the timeout() signal

    connect( &m_timerDlgBlownUpParamDisplay, SIGNAL(timeout()), this, SLOT(closeDlgBlownUpParamDisplay()) );
    

    On Qt5.8 on DEBIAN8 (with gnome) the program works fine now. But when I compile an run it in my WIN10 virtualBox with Qt5.7 the program just crashes when I activate the display of the BlownUp window. Weird !

    alt text



  • After checking, I also get an error message in the Application Output in WIN10/QT5.7 environnement:

    ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 1bde7af0. Receiver 'verticalSlider_1' (of type 'QSliderMod1') was created in thread 1858c390", file kernel\qcoreapplication.cpp, line 541

    which does not appear in DEBIAN8/QT5.8 environment. --> Maybe it is a bug in Qt version 5.7 that was fixed 5.8 ?


  • Qt Champions 2016

    Run your program through the debugger, break at the assertion, inspect the stack trace. Also the message is pretty descriptive, you're sending events from a thread different from main (or at least I assume it's the main thread that creates the slider).

    Maybe it is a bug in Qt version 5.7 that was fixed 5.8 ?

    I really doubt it. Check your code.



  • Thanks for your help kshegunov (and thanks you to dheerendra for the first answer)

    Here is the call stack after breaking on (before) the Assertion :

    1  QCoreApplicationPrivate::checkReceiverThread    qcoreapplication.cpp                533  0xbb52f6   
    2  QApplication::notify                            qapplication.cpp                    3021 0x5a8a44   
    3  QCoreApplication::notifyInternal2               qcoreapplication.cpp                988  0xbb6187   
    4  QCoreApplication::sendEvent                     qcoreapplication.h                  231  0xe425b3   
    5  QWidgetPrivate::propagatePaletteChange          qwidget.cpp                         2016 0x568e2c   
    6  QWidgetPrivate::setPalette_helper               qwidget.cpp                         4654 0x56e6e6   
    7  QWidget::setPalette                             qwidget.cpp                         4584 0x56e364   
    8  MIDIControllerGenericDialogMOD1::colorSlider    midicontrollergenericdialogmod1.cpp 466  0x42a07b   
    9  MIDIControllerGenericDialogMOD1::setSliderValue midicontrollergenericdialogmod1.cpp 404  0x429c2e   
    10 MIDIControllerGenericDialogMOD1::setValue       midicontrollergenericdialogmod1.cpp 628  0x42a932   
    11 MidyAX::processSYSEXReceivedFromAXEFX           midyax.cpp                          2089 0x40d087   
    12 midiCallback_AXEFX                              midyax.cpp                          667  0x406f43   
    13 midiInputCallback                               RtMidi.cpp                          2067 0x4137f6   
    14 WINMMBASE!DriverCallback                                                                 0x14db27   
    15 midMessage                                                                               0x5d5ff990 
    16 midMessage                                                                               0x5d5fff6c 
    17 midMessage                                                                               0x5d5ffe0d 
    18 midMessage                                                                               0x5d5ffb3c 
    19 KERNEL32!BaseThreadInitThunk                                                             0x76ff8e94 
    20 ntdll!RtlDestroyQueryDebugBuffer                                                         0x779b9bc3 
    21 ntdll!RtlDestroyQueryDebugBuffer                                                         0x779b9b92 
    22 ??                                                                                                  
    

    at level 13 is the function that receive the MIDI message from Win10. My code stops at level 8 to color the slider. All this is going on in thread 5 and thread 1 (of 8) seems to be the main thread :

    1  QMetaObject::inherits                                            qmetaobject.cpp                339   0xbade85   
    2  QMetaObject::cast                                                qmetaobject.cpp                366   0xbadeda   
    3  QMetaObject::cast                                                qmetaobject.cpp                355   0xbadea9   
    4  qobject_cast<QAbstractButton *>                                  qobject.h                      516   0xd8c46a   
    5  QWindowsXPStyle::polish                                          qwindowsxpstyle.cpp            1317  0x72f6e0   
    6  QWindowsVistaStyle::polish                                       qwindowsvistastyle.cpp         2303  0x74ce27   
    7  QWidget::event                                                   qwidget.cpp                    8876  0x579b61   
    8  QAbstractSlider::event                                           qabstractslider.cpp            958   0x504239   
    9  QSlider::event                                                   qslider.cpp                    359   0x4a39c8   
    10 QApplicationPrivate::notify_helper                               qapplication.cpp               3799  0x5ab73e   
    11 QApplication::notify                                             qapplication.cpp               3762  0x5ab584   
    12 QCoreApplication::notifyInternal2                                qcoreapplication.cpp           988   0xbb6187   
    13 QCoreApplication::sendEvent                                      qcoreapplication.h             231   0xe425b3   
    14 QWidget::ensurePolished                                          qwidget.cpp                    10018 0x57b2f7   
    15 QWidget::ensurePolished                                          qwidget.cpp                    10027 0x57b36f   
    16 QWidget::ensurePolished                                          qwidget.cpp                    10027 0x57b36f   
    17 QWidget::ensurePolished                                          qwidget.cpp                    10027 0x57b36f   
    18 QWidget::event                                                   qwidget.cpp                    8872  0x579b3b   
    19 QMainWindow::event                                               qmainwindow.cpp                1543  0x4c8b46   
    20 QApplicationPrivate::notify_helper                               qapplication.cpp               3799  0x5ab73e   
    21 QApplication::notify                                             qapplication.cpp               3762  0x5ab584   
    22 QCoreApplication::notifyInternal2                                qcoreapplication.cpp           988   0xbb6187   
    23 QCoreApplication::sendEvent                                      qcoreapplication.h             231   0xe425b3   
    24 QCoreApplicationPrivate::sendPostedEvents                        qcoreapplication.cpp           1649  0xbb73d3   
    25 QEventDispatcherWin32::sendPostedEvents                          qeventdispatcher_win.cpp       1294  0xb774c6   
    26 QWindowsGuiEventDispatcher::sendPostedEvents                     qwindowsguieventdispatcher.cpp 81    0x7a1677   
    27 qt_internal_proc(HWND__ *, unsigned int, unsigned int, long) *16 qeventdispatcher_win.cpp       443   0xb74782   
    28 USER32!RecordShutdownReason                                                                           0x75418233 
    29 USER32!DispatchMessageW                                                                               0x753ee638 
    30 USER32!DispatchMessageW                                                                               0x753ee103 
    31 USER32!DispatchMessageW                                                                               0x753edf50 
    32 QEventDispatcherWin32::processEvents                             qeventdispatcher_win.cpp       844   0xb75d0e   
    33 QWindowsGuiEventDispatcher::processEvents                        qwindowsguieventdispatcher.cpp 74    0x7a1645   
    34 QCoreApplication::processEvents                                  qcoreapplication.cpp           1206  0xbb6667   
    35 delay                                                            midyax.cpp                     1997  0x40ca37   
    36 MidyAX::getMidiMappingParamValues                                midyax.cpp                     1505  0x409e9d   
    37 MidyAX::init                                                     midyax.cpp                     316   0x404fd0   
    38 MidyAX::MidyAX                                                   midyax.cpp                     110   0x40294e   
    39 qMain                                                            main.cpp                       15    0x4016b5   
    40 WinMain *16                                                      qtmain_win.cpp                 123   0x43c290   
    41 main                                                                                                  0x1148ecd
    

    The color slider event is not in the main thread and the slider has been created in the main thread. But the same code has been working before on Qt5.7/WIN10 and is working in DEBIAN8/Qt5.8, isn't that a bit abnormal ? or not.

    I tried to get the call stack in the DEBIAN8/Qt5.8, but I can't get the program to breack --> must be a set-up issue were the debugger is not talled to use the debug lib or some other config problem. Not sure what to check in Qt Creator.


  • Qt Champions 2016

    @PALYGAP said in Problem with QTimer --> get message "Object::startTimer: Timers cannot be started from another thread":

    The color slider event is not in the main thread and the slider has been created in the main thread.

    Whence the warning.

    But the same code has been working before on Qt5.7/WIN10 and is working in DEBIAN8/Qt5.8

    So? You have been depending on an implementation detail and got bitten, I don't see anything strange in this.

    isn't that a bit abnormal ?

    Not at all, refer to this quotation from the docs:

    As mentioned, each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread. All widgets and several related classes, for example QPixmap, don't work in secondary threads. A secondary thread is commonly referred to as a "worker thread" because it is used to offload processing work from the main thread.

    You can't use widgets (here the polish events are the trigger) from different threads. It's not supported, it won't be supported and if it works then it's an implementation detail. You must serialize all GUI related events through the main thread's event loop. It is apparent from the stack traces you have a race condition deep in Qt's code, but that's because you're relying on an undocumented behaviour. You need to fix your code so all access to the widgets' routines is done from the main thread.



  • So? You have been depending on an implementation detail and got bitten, I don't see anything strange in this.

    Did know I was doing that ... but that hurts anyway. :)

    Thanks for the tips.

    I might go back to the original design were the MIDI incoming events were dispatched to the main application window. Looks like that way the access the widget (sliders) was done in the main thread.


  • Qt Champions 2016

    @PALYGAP said in Problem with QTimer --> get message "Object::startTimer: Timers cannot be started from another thread":

    Did know I was doing that ... but that hurts anyway. :)

    Truth hurts, man. ;)

    I might go back to the original design were the MIDI incoming events were dispatched to the main application window. Looks like that way the access the widget (sliders) was done in the main thread.

    Well, what about processing your events in the worker thread and from there raising a number of signals that are connected to the GUI? That's the usual way to do such things - process whatever's heavy and blocking and only notify the widgets about things they have to display (e.g. a progress bar change or something like this).



  • @kshegunov said in Problem with QTimer --> get message "Object::startTimer: Timers cannot be started from another thread":

    Well, what about processing your events in the worker thread and from there raising a number of signals that are connected to the GUI? That's the usual way to do such things - process whatever's heavy and blocking and only notify the widgets about things they have to display (e.g. a progress bar change or something like this).

    Good idea, didn't thought about it. I'll try it tomorrow.
    Thanks.



  • I did the changes. Now the program sends events from the "worker" thread that process the incoming MIDI messages. It is now working fine in WIN10.

    Thanks for your help kshegunov


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.