2
votes

Despite not using std::thread or QThread anywhere, still getting following problems:

  1. Always a runtime debug error log from Qt:

    QObject::connect: Cannot queue arguments of type 'QAbstractSocket::SocketError'
    (Make sure 'QAbstractSocket::SocketError' is registered using qRegisterMetaType().)

  2. Intermittent crash on TcpSocket::flush() method;
    I use this method to make sure that the TCP is written immediately; Now sometimes the app crashes exactly at this method with SIGPIPE

Upon searching internet, found that people suggest that to fix 1st problem (i.e. the meta error), I need to register using qRegisterMetaType(), when we have multiple threads.
Same multithreading is referred as a cause for the 2nd problem as well; see this and this.

But I don't have more than 1 thread!
My socket code looks like below:

struct Socket : public QSslSocket
{
  Q_OBJECT public:

  void ConnectSlots ()
  {
    const auto connectionType = Qt::QueuedConnection;
    connect(this, SIGNAL(readyRead()), this, SLOT(ReceiveData()), connectionType);
    connect(this, SIGNAL(disconnected()), this, SLOT(Disconnected()), connectionType);
    connect(this, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(Error(QAbstractSocket::SocketError)), connectionType);
    //                           ^^^^^^^ error comes whether I comment this or not
  }

  public slots:
  void ReceiveData () { ... }
  void Disconnected () { ... }
  void Error () { ... }
}

Question: Is Qt creating any internal thread by itself for read/write purpose? (I hope not). How to fix above 2 issues?

2
The first thing that came into my mind is using int instead of enum type in your signal/slot connection. I.e. ...SLOT(Error(int)... if you don't want to bother yourself with Qt meta system, and properly cast parameters to enum values in your slot.vahancho
You have a comment in the code shown "error comes whether I comment this or not". Comment out what specifically -- just the QAbstractSocket::SocketError parameter to Error or the entire call to to connect?G.M.
@vahancho, if I make it Error(int) then there is an error of incompatible type: "QObject::connect: Incompatible sender/receiver arguments. Connection::Socket::error(QAbstractSocket::SocketError) --> Connection::Socket::Error(int)". Suppose if I remove argument and make it Error(), then the actual problem mentioned in Qn still persists.iammilind
Why do you need QueuedConnection? Usually I am using default AutoConnectionJeka
@Jeka, because many a times when the function of "Write()" is called, before it finishes "Read()" is invoked in between. The logic of my code expects that "Write()" should be fully finished before "Read()" starts and vice versa. With QueuedConnection that part was resolved.iammilind

2 Answers

3
votes

I don't think the problem is related to threads, rather it's the combination of a QAbstractSocket::SocketError type parameter and Qt::QueuedConnection that's causing the issue.

Looking at the the various connect implementations in the Qt5.8 source, if Qt::QueuedConnection is specified as the connection type then a check against the signal's parameter types will be performed. Something like...

int *types = 0;
if ((type == Qt::QueuedConnection)
        && !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) {
   return QMetaObject::Connection(0);
}

where queuedConnectionTypes will return a null pointer if any of the types are not registered.

So, if the connection is queued then all parameters used by the signal must be registered regardless of whether or not they are used by the slot. To avoid the error make sure you call...

qRegisterMetaType<QAbstractSocket::SocketError>();

once at some point before any calls to connect that use the combination of a QAbstractSocket::SocketError parameter and Qt::QueuedConnection.

0
votes

No, the sockets don't create a separate thread for read/write. Instead the OS raises the event on a given socket descriptor, whenever there is a read/write observed. This event should be queued. Hence for that Qt::QueuedConnection is preferred.

The QAbstractSocket::SocketError is impromptu and appears OS specific. It cannot be avoided. At the max, the socket can be destroyed when such error happens.

To avoid crashes, whenever there is a disconnection in the socket, following can be done:

void Destroy (QWebSocket* const pSocket)
{
  if(pSocket == nullptr)
    return;
  pSocket->disconnect();  // no further signal/slot
  pSocket->close();  // graceful closure
  pSocket->deleteLater(); // don't delete immediately; let the Qt take care
  pSocket = nullptr; // to avoid further undefined behaviour
}

Even after doing above, sometimes a socket crash happens due to write() operation. viz. When the socket close()-es, it tries to flush() all the writable data. During then, if the remote connection is already closed, then the OS crashes the program using a SIGPIPE event. Unfortunately it cannot be prevented in C++ using the std::exceptions.

The solutions mentioned in below post doesn't help:
How to prevent SIGPIPEs (or handle them properly)
This can be avoided by following:

if(pSocket->isValid())
  pSocket->sendBinaryMessage(QByteArray(...));

So isValid() helps in a situation, where a socket is trying to write something into an already disconnected remote socket connection.