6

According to following post an emitted signal is served, only once the currently executing slot completes.
Wait for a SLOT to finish the execution with Qt

I have a client-server communication app based on ssl socket, which is single threaded.

connect(socket, &QSslSocket::readyRead, [&]() { myObject.Read(); });

Client & server send each other some custom messages. Whenever a message is sent or received by either, they send ACK bytes (00).
Most of the times, I notice that when the Read() is in between of execution, the next readyRead() is served! I put debug statements in beginning & end of myObject->Read(). They confirm that, beginning debug is called again & again. Same is observed with breakpoints.

When too much of data is received, a recursive stack frame is created of too many Read()s. It either slows down the app GUI or crashes.
Typically this recursion happens, when client attempts to send an ACK as part of myObject->Read(). During that time readyRead() is incidentally signalled & also gets served. However the slot of previous signal was still under processing.

Questions:

  • Is it possible for Qt framework to cater a signal in between when a slot is still mid-way (single thread)?
  • How to fix this socket specific scenario?

Note:
- By default for single threads, the Qt::ConnectionType is DirectConnection. I have tried with QueuedConnection as well, but the result is same.
- myObject.Read() is quite complex and has many other function calls. If this is causing problem, then let me know what should I look for. It will be impractical to write the actual code of it.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • 1
    It may help https://stackoverflow.com/questions/11230080/qobject-based-class-has-a-queued-connection-to-itself – Amol Saindane Oct 05 '17 at 05:21
  • If `myObject->Read()` is called while you are in already in it, it is likely that you have at some point given control to the event loop. Can you add the code of the function? – Benjamin T Oct 05 '17 at 05:54
  • @BenjaminT, there are lots of things which are not related to Qt but core functionality inside the `Read()` method. It will be impractical to write that code as there are several methods inside that. However, how is it possible for an event loop can take control when a slot is still under execution? Can you suggest, in which ways is this possible. May be I can try looking at those. – iammilind Oct 05 '17 at 06:36
  • @SamuraiJack, thanks for the linked post. I tried that, but it seems that both `DirectConnection` and `QueuedConnection` result in the same behaviour. Is there anything, which can cause this issue? Please see the updated Qn as well. – iammilind Oct 05 '17 at 06:52
  • Add a recursion counter to `Read` -- increment on entry, decrement on exit -- and also some code that executes only if the counter is greater than one. If you then run under a debugger with a breakpoint on the recursion check code, the backtrace should at least let you know how you got there. – G.M. Oct 05 '17 at 07:53
  • @G.M., I have tried that, it didn't give any advantage though. However, it's evident that, the `Socket::readyRead()` is called at any arbitrary time. That's ok, but somehow it's catered immediately even when the earlier SLOT hasn't finished yet. Is there any way to check if the event loop was free in between (I don't think it's possible though). – iammilind Oct 05 '17 at 08:54
  • 1
    @iammilind: as you said you have lot of function call in myObject.Read(), you can use QtConcurrent::run function. It allows you to run a function asynchronously. Link - http://doc.qt.io/qt-4.8/qtconcurrentrun.html – Amol Saindane Oct 05 '17 at 12:28
  • @SamuraiJack, thanks I can try that. As of now, I notice that I have lots of sqlite operations, which make the GUI sluggish. If I disable them then the GUI is much faster. Can I use this `QtConcurrent::run()` feature to execute this individual sqlite operations (i.e. `INSERT, UPDATE`)? Will it create a thread every time (time consuming) or will it keep a thread in a pool for future use for better performance? Please suggest. – iammilind Oct 05 '17 at 12:48
  • @iammilind: I never tried specific to sqlite, but you can give it a try. As per documentation function runs in a separate thread, but its upto you, how you manage sqlite operations in it. – Amol Saindane Oct 05 '17 at 12:53

1 Answers1

2

The recursive calls of readyRead() were happening because of the event loop getting freed up in between. Following functions were causing the event loop to be freed:

  1. QCoreApplication::processEvents()
  2. SslSocket::flush()

The 1st is understandable, as it's meant for that. But the 2nd flush() was a total surprise. Its documentation doesn't state so. At least in my debugging it was showing that, whenever the flush() is invoked, the subsequent readyRead() is invoked and catered. See that in the Qn as well.

The processEvent() was meant to make GUI more responsive during the high loading of the data. But it seems that, we need to make another choice for the same.

iammilind
  • 68,093
  • 33
  • 169
  • 336