Qt error with QSerialPort in multithreaded code: QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread

knocker_d picture knocker_d · May 30, 2013 · Viewed 7.5k times · Source

deleting serialPort causes this message in debug builds:

ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread c0a528. Receiver '' (of type 'QSerialPort') was created in thread c76850", file kernel\qcoreapplication.cpp, line 532

The actual error is triggered in ~QSerialReader when Main window is closed. Here's the code.

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    runnerThread=new QThread(this);    
    /*  - connect thread start signal to qrunnerthread's runLoop()
        this starts the main job loop            
        - connect thread finished signal to mainwindows' deleteLater
        to ensure proper deletion */
    qDataReader = new QDataReader();
    connect(runnerThread, SIGNAL(started()), qRunnerThread, SLOT(runLoop()));    
    connect(runnerThread, SIGNAL(finished()), this, SLOT(deleteLater()));
    qDataReader->moveToThread(runnerThread);
    runnerThread.start();
}

MainWindow::~MainWindow()
{
    runnerThread->exit();
}

//slot that runs when thread is started
void QDataReader::runLoop() {
    /* this code runs in different thread than QDataReader */    
    //serialPort parent will be QThread (runnerThread)
    serialReader=new QSerialReader(this);        

    while(doStuff) {    
        QString data=serialReader.readData();
        emit dataReceived(data);    
    }
}

QDataReader::~QDataReader() {
    delete runner;
}

QSerialReader::QSerialReader(QObject* parent) {
    //serialPort parent will be QSerialReader
    serialPort = new QSerialPort(this);
}

QSerialReader::~QSerialReader() {   

    /*
    deleting serialPort causes this message in debug builds:

    ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread c0a528. Receiver '' (of type 'QSerialPort') was created in thread c76850", file kernel\qcoreapplication.cpp, line 532
    */
    delete serialPort;  
}

edit1: peppe's suggestion:

If I change QSerialReader to a member in QDataReader the problem is gone. BUT when opening port with QSerialPort::open this error comes up:

QObject: Cannot create children for a parent that is in a different thread. (Parent is QSerialPort(0xdf6930), parent's thread is QThread(0xd8a528), current thread is QThread(0xdf6850) QObject: Cannot create children for a parent that is in a different thread. (Parent is QSerialPort(0xdf6930), parent's thread is QThread(0xd8a528), current thread is QThread(0xdf6850)

QSerialPort::open(xx) is called inside serialReader.readData();

So still... some nasty problem. I did not have issues like this when I coded in Java a few years ago.

stack trace when CSerialReader::openPort is called. this calls QSerialPort::open. Clearly this is run in other thread than main.

0   CSerialReader::openPort serialreader.cpp    480 0xff7211    
1   CSerialReader::readData serialreader.cpp    209 0xff4e1d    
2   QDataReader::runLoop    qdatareader.cpp 138 0xffa3fe    
3   QDataReader::qt_static_metacall moc_qdatareader.cpp 62  0x1040824   
4   QMetaObject::activate   qobject.cpp 3539    0x669b0e14  
5   QThread::started    moc_qthread.cpp 113 0x66a29979  
6   QThreadPrivate::start   qthread_win.cpp 345 0x66816918  
7   _callthreadstartex  threadex.c  314 0xf71a293   
8   _threadstartex  threadex.c  297 0xf71a224   
9   BaseThreadInitThunk kernel32        0x74cd3677  
10  __RtlUserThreadStart    ntdll32     0x76f9c002  
11  _RtlUserThreadStart ntdll32     0x76f9bfd5  

stack trace when CSerialReader is created (as member variable). This is called in main thread.

0   CSerialReader::CSerialReader    Runner.cpp  48  0x9d3d14    
1   QDataReader::QDataReader    qdatareader.cpp 10  0x9d8fa4    
2   MainWindow::MainWindow  mainwindow.cpp  72  0x9c61eb    
3   main    main.cpp    105 0x9c4adc    
4   WinMain qtmain_win.cpp  131 0xa273ba    
5   __tmainCRTStartup   crtexe.c    547 0xa269e0    
6   WinMainCRTStartup   crtexe.c    371 0xa2676f    
7   BaseThreadInitThunk kernel32        0x74cd3677  
8   __RtlUserThreadStart    ntdll32     0x76f9c002  
9   _RtlUserThreadStart ntdll32     0x76f9bfd5  

Answer

peppe picture peppe · May 30, 2013
serialReader=new QSerialReader(this);

Don't do this. Parents and children must live in the same thread. In your case, the parent (QThread) is living in its own thread, and the child (QSerialReader) is living in the thread you spawned. Can't you just allocate it on the stack instead?