Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QAbstractItemModel asserts & multithreading



  • Hey

    I'm trying to solve a problem with these 2 aseserts :

    ######
                            FATAL: ASSERT: "last < rowCount(parent)" in file itemmodels\qabstractitemmodel.cpp, line 2782
     (itemmodels\qabstractitemmodel.cpp:2782, (null))
    ######
    
    
    ######
                            FATAL: ASSERT: "!this->isEmpty()" in file c:\users\qt\work\qt\qtbase\include\qtcore\../../src/corelib/tools/qstack.h, line 62
     (c:\users\qt\work\qt\qtbase\include\qtcore\../../src/corelib/tools/qstack.h:62, (null))
    ######
    

    I've recently moved a lot of my logic to thread and when I load data all that happens in thread now. I'm not sure how to bite it as all I got are these 2 crash lines and
    d0aa29f9-0233-43af-a5c4-0ffb28082d04-image.png

    The error does not happen in debug-Release mode, only in debug mode...

    How can I bite it ?

    Regards
    Dariusz


  • Lifetime Qt Champion

    A model must be in the same thread as the view(s) - so it must be in the main thread. If this is not the case you must add QMutexes (or similar) to protect access from two different threads.



  • Yeah I've just dig in to it and found out the error location...

    It was beginRemoveRows/endRemoveRows being called from another thread while QporxyModel/filterModel were trying to refresh/update data...

    So I've learned that Q_EMIT beginRemoveRows() does not do what I though it did when it comes to signals..

    The signal is being called from Thread & executed from thread... but I need to force it to main thread, how can I do this ?

    Currently I'm thinking that I could do this >

    		QMetaObject::invokeMethod(qApp, [&]() { beginRemoveRows(index(),row,row);// + other code to finish the action... },Qt::BlockingQueuedConnection);
    

    But does this not have a lot of overhead? How can I handle case like this?

    Should I make my own signal/slot like

    signal void beginRemoveRowsThreaded(Const QModelIndex&index, const int start, const int right end);
    
    slot inline void handleRemoveRowsThread(Const QModelIndex&index, const int start, const int right end){Q_EMIT beginRemoveRows(index.start,end);}
    

    and do connection

    connect(this,&item::beginRemoveRowsThreaded,this,&item::handleRemoveRowsThread,Qt::BlockingQueuedConnection);
    
    

    and then call the internal signal to force it to main thread ? Will that be more efficient than the QMetaObject::invoke ?

    TIA


  • Lifetime Qt Champion

    I would send a signal to the model so it can fetch the new data from the thread - looks the easiest and cleanest solution for me - esp. since this will also work when the data fetching is not done in a separate thread, the logic will not change.



  • @Christian-Ehrlicher said in QAbstractItemModel asserts & multithreading:

    I would send a signal to the model so it can fetch the new data from the thread - looks the easiest and cleanest solution for me - esp. since this will also work when the data fetching is not done in a separate thread, the logic will not change.

    Hmmmmmmmmmm I'm lost :- D So do I disable some automatic fetching and then configure manual fetching? I'm lost o.O how do I do dis?


  • Lifetime Qt Champion

    What to you mean with automatic/manual fetching?



  • @Christian-Ehrlicher I would send a signal to the model so it can fetch the new data from the thread
    Hmmm I think this might not work for me. Feels like I need it to work on per-item level. Essentially force any call changing child count to be main thread-based.


  • Lifetime Qt Champion

    @Dariusz said in QAbstractItemModel asserts & multithreading:

    Hmmm I think this might not work for me

    Why not? It makes no difference if you push or pull the data... but it's up to you :)


Log in to reply