Alexander Kondratskiy
Alexander Kondratskiy

Reputation: 4275

Qt Socket blocking functions required to run in QThread where created. Any way past this?

The title is very cryptic, so here goes!

I am writing a client that behaves in a very synchronous manner. Due to the design of the protocol and the server, everything has to happen sequentially (send request, wait for reply, service reply etc.), so I am using blocking sockets. Here is where Qt comes in.

In my application I have a GUI thread, a command processing thread and a scripting engine thread. I create the QTcpSocket in the command processing thread, as part of my Client class. The Client class has various methods that boil down to writing to the socket, reading back a specific number of bytes, and returning a result.

The problem comes when I try to directly call Client methods from the scripting engine thread. The Qt sockets randomly time out and when using a debug build of Qt, I get these warnings:

QSocketNotifier: socket notifiers cannot be enabled from another thread
QSocketNotifier: socket notifiers cannot be disabled from another thread

Anytime I call these methods from the command processing thread (where Client was created), I do not get these problems.

To simply phrase the situation:

Calling blocking functions of QAbstractSocket, like waitForReadyRead(), from a thread other than the one where the socket was created (dynamically allocated), causes random behaviour and debug asserts/warnings.

Anyone else experienced this? Ways around it?

Thanks in advance.

Upvotes: 4

Views: 4541

Answers (1)

spbots
spbots

Reputation: 1553

I'm assuming that you're using QThread for the threading operations. If so, you can either a) use queued signal-slot connections; or b) explicitly use the QThread's event loop by creating a custom event type, posting that event in your scripting engine, and then having the client class handle those events.

example for a)

class ClientThread : public QThread
{
    ..stuff..
public slots:
   waitForReadyRead();
};

class ScriptEngineThread : public QThread
{
    ..other stuff..
 signals:
    void requestWaitForReadyRead();
};

// In the ScriptEngineThread implementation...
ScriptEngineThread::setupClient()
{
   connect(this, SIGNAL(requestWaitForReadyRead()),
      client_, SLOT(waitForReadyRead()),
      Qt::QueuedConnection);
}

Then, whenever you want to do the socket operations, just emit ScriptEngineThread::requestWaitForReadyRead();. The major difficulty is, I assume, you need the scripting thread to wait for some socket operation to be done. In this case you'll need to fire signals back and forth between the threads, causing some circular dependancy.

For alternative b, it would require a little more coding work, but you could:

  • Create your own subclasses of QEvent
  • Create a class to operate as a middleman, posting and handling each of your QEvents and emitting signals to communicate with the threads - each thread can have an instance of this
  • Connect the client and script engine to the event signals it cares about.

Upvotes: 4

Related Questions