Qt models and multi-threaded inserts
Hi,
Say I have a model subclassed from QAbstractTableModel, which is shown in standard tablewidget.
The model models a global datastore in my program, but this store is internally threadsafe, and it's assumed that multiple threads will modify the data in it, which can change the rowCount() in the model.
I am now wondering how to do this safely. Sure, even in the ideal case, there might be a more or less short period where a view could display outdated data or empty rows or such.
That's fine, but I want a guarantee it wont lead to corruption or crash.
It's clear the begin/endResetModel methods in the model cant be called from another thread.
But then what is the right way to do it?
1) Even if i lock it somehow, is it sufficient to check the row/index in the data() method in order to avoid oob crashes? It should be, right, since the model abstracts my global store, and no other place accesses my global store.
The danger is though that Qt internally implicitly assumes or asserts that all QModelIndex it ever encounters are always QModelIndex::row() < MyModel::rowCount(). I see no guarantee this is not the case.
2) How to communicate the change to the backing store from thread X to the model/view living in the main thread Y.
I suppose a queued signal-slot connection is the easiest way. That is, introduce a new thread_local notification class that lives on thread X, and connect two of its signals to the begin/endResetModel() slots of the model in thread Y.
That way the change is communicated reasonably quickly, and because I check the size in data() (after locking the store), the worst thing that can happen is an empty row or outdated data in the view for a moment.
Problem is that beingResetModel() and endResetModel() will potentially+probably both be processed *after* the modification is already made to the backing store. But that should be ok bc of the first question.
Right?
Thanks :)
Re: Qt models and multi-threaded inserts
These two statements seem inconsistent to me:
Quote:
The model models a global datastore in my program, but this store is internally threadsafe, and it's assumed that multiple threads will modify the data in it
Quote:
the model abstracts my global store, and no other place accesses my global store.
If your global store has only one client (second statement), why do you say multiple threads will modify it (first statement)? Do you mean to say that in your program, your model is the only accessor of the data store, but there could be other programs that are also accessing and possibly modifying it?
You might have to implement more than just the beginResetModel / endResetModel methods - like those for row insertion and removal - unless you want to completely reload the table view every time the database changes.
Presumably the QSqlTableModel and QSqlTableView classes have been implemented to handle the scenario you are describing. It might be worthwhile to study that source code for ideas. If your data store is based on an SQL DB, then you might be able to use those classes instead of rolling your own.
Re: Qt models and multi-threaded inserts
Thanks. I meant from a Qt point of view, the store is accessed by different parts of the program (including external plugins, which it turns out sometimes do so from threads).
Honestly I was not going to reimplement anything, just calling the existing methods to trigger the data refresh.
It's true that this would reload the table view, but otherwise I imagine it would not be possible to reliably delete or refresh a specific row via queued connections, give the nature of the inter-thread queuing.
I will look a at the code of those modes. You can imagine my datastore like a global map protected by a mutex, and modified by different threads.
I am now thinking: Maybe it's better to instead intercept modifications to the store from non-main threads, and queue those modification into the main thread via a blocking queued signal+slot. That way the model on the mainthread can do the actual del/insert, including emitting proper refresh notices. Store modifications from non-main threads get more expensive, but I expect they are rare anyways. Is it a good idea?
Re: Qt models and multi-threaded inserts
Quote:
Is it a good idea?
Yes, it seems like this would be a good way to ensure that your UI would always reflect the current state of the data store if all modifications went through your model. Are your program and the plugins the only way to modify your data store? If yes, then it would simplify things if all modifications could go through your model. Since modifications would be queued, I do not think the state of your data store could get out of sync. You might have to lock reads whenever a write is in progress to ensure that no client got a bad record.