Strange signal handling (nested eventloops)
-
(Working with Qt 5.15.2)
I have a method which receives a signal (sig_responseReady) from the same thread. The purpose of this method is to wait until a response is received. While this method is waiting for the signal, the method is called again because of an event which calls this method (so eventloops get nested). However, the first waitForResponse on the stack does not get the signal for up to 10 seconds (for no reason I can see), while later called waitForResponse get their signal first.The result of this design is that I have nested eventloops, which according to this stackoverflow post causes slot/signal handling errors, and should be avoided. I suspect this is the cause of my problem.
Is there a design which accomplishes the same, with only a single eventloop? (I can't come up with it) The function which calls waitForResponse must pause until waitForResponse returns. ..I can't make it asynchronous. I tried replacing eventloop.exec with QCoreApplication::processEvents(QEventLoop::AllEvents,100), but still get same strange result. (So maybe nested event loops is not the problem...but I can't figure out the cause)
MyClass::SRequest MyClass::waitForResponse(const QUuid queryID) { reentryCount++; // Static var to determing nesting/depth count QEventLoop eventloop; qDebug() << "MyClass::waitForResponse connect for uid " << queryID << ", depth " << reentryCount; connect(this,&MyClass::sig_responseReady,&eventloop, &QEventLoop::quit); //, Qt::QueuedConnection); // Allow matching response to stop eventloop do { eventloop.exec(); qDebug() << "MyClass::waitForResponse got signal for uid " << queryID; } while (!queryResponseReady(queryID) && !queryExpired(queryID)); qDebug() << "MyClass::waitForResponse exitted loop for signal for uid " << queryID << ", depth " << reentryCount; reentryCount--; return queryTakeResult(queryID); }
-
@ocgltd said in Strange signal handling (nested eventloops):
The purpose of this method is to wait until a response is received.
This is wrong. Don't do this - use Signals and Slots.
Calling QEventLoop is strongly discouraged because of the problems you see above (and others - e.g. another signal can kill your object inside the newly created eventloop) - fix your design. -
This code (MyClass) is part of a descendant of QAbstractItemModel. MyClass retrieves data from a DB (eg rowcount) from another thread by sending a request signal, and then receiving a response signal.
So in my overriden rowCount method I must keep an eventloop running to receive the incoming response signal, but wait there until receiving an answer - so it must process events That's the purpose of waitForResponse method). But this allows a second rowCount method to be called in the same thread, so I get overlapping waitForResponse's running.
This is clearly a design problem...but I can't figure out a solution. Any recommendation on the right way to design this? (so I don't have overlapping eventloops)
-
@ocgltd said in Strange signal handling (nested eventloops):
So in my overriden rowCount method I must keep an eventloop running to receive the incoming response signal, but wait there until receiving an answer
No, this can and will not work. You really must not create an eventloop inside rowCount().
Pass your data from the db thread to the main thread via signals and slots and properly call begin/endInsertRows() there. -
@Christian-Ehrlicher I think you are suggesting that I create a descendant of QAbstractItemModel model. And since each of the functions that must be overridden (eg: rowCount, data) must return information immediately, I must return some form of dummy data that later gets replaced with real data when my DB thread returns answers?
...I'm new to QAbstractItemModel so trying to wrap my head around how to make it asynchronous..
-
@ocgltd said in Strange signal handling (nested eventloops):
that I create a descendant of QAbstractItemModel model.
You for sure have to derive from QAbstractItemModel -how else will you return the data otherwise?
-
@Christian-Ehrlicher Yes I phrased that poorly :)
In order to make QAbstractItemModel asynchronous, I think I will have to create a caching mechanism. The overridden functions (like rowCount, data) will have to return fake results immediately with some type of indicator that data is not cached, and once the real data arrives from the DB thread I will have to emit a data changed signal for that parent/row/col.
Seems like something that someone would have already done...
-
@ocgltd said in Strange signal handling (nested eventloops):
will have to return fake results
No, no fake results. It simply returns the rows which are already known. No need to fake anything.
btw: What should a db thread do when you wait for it in the gui thread anyway?
-
@Christian-Ehrlicher The DB thread will be waiting for the SQL transactions to complete (on a very slow DB)
-
@ocgltd The point from @Christian-Ehrlicher is that you're waiting in main thread for the db thread to complete, so there is no point to have a thread at all.
-
@ocgltd said in Strange signal handling (nested eventloops):
Since the db connection cannot be shared between threads
Each thread can have its own db connection