Search code examples
c++qtnetwork-programmingsignals-slots

Qt - QLocalSocket Signal-Slot not working resulting in deadlocks in destructor


I am using QLocalSocket and QLocalServer for inter process communication on Windows 7 using VS 2010 and Qt 5.5.1.

After sending over 256 messages to the other process the destructor in CIPSocket freezes. I traced the problem to a signal-slot problem in qtbase\src\corelib\ioqwinoverlappedionotifier.cpp where the emitted signal _q_notify() in notify(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped) does not result in a call of _q_notified(). Therefore the Semaphore hSemaphore exceeds its max-count, resulting in the deadlock in the destructor.

What could be the reason for the signal-slot not working? I could not find any disconnects or block signals.

Thanks in advance.

main.cpp:

#include "main.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
#include <iostream>

int main(int argc, char *argv[])
{
    printf("Server (0) or Socket (1)?\n");
    char c = getchar();
    if (c == '0') {
        QCoreApplication app(argc, argv);
        CIPServer server;
        app.exec();
    }
    else if (c == '1') {
        CIPSocket socket;
        for (unsigned int i = 0; i <= 256; ++i) {
            socket.update(i);
            QThread::msleep(10);
        }
    }
}

/*--------------------------------------------------------------------------
   CIPSocket
----------------------------------------------------------------------------*/
CIPSocket::CIPSocket()
: m_bIsReady(false)
{
    m_pSocket = new QLocalSocket(this);
    m_stream.setDevice(m_pSocket);

    connect(m_pSocket, SIGNAL(connected()), this, SLOT(connectionReady()));
    connect(m_pSocket, SIGNAL(disconnected()), this, SLOT(connectionLost()));

    m_pSocket->connectToServer("DemoServer");
}

CIPSocket::~CIPSocket()
{
    delete m_pSocket;
    m_pSocket = NULL;
}

void CIPSocket::update(int i)
{
    if (m_bIsReady)
        m_stream << i;
}

void CIPSocket::connectionReady()
{ m_bIsReady = true; }

void CIPSocket::connectionLost()
{ m_bIsReady = false; }

/*--------------------------------------------------------------------------
   CIPServer
----------------------------------------------------------------------------*/
CIPServer::CIPServer(QObject* parent)
: QLocalServer(parent)
{
    if (!listen("DemoServer")) {
        throw ("Could not connect to 'DemoServer'");
    }
    connect(this, SIGNAL(newConnection()), this, SLOT(socketConnected()));
}

CIPServer::~CIPServer()
{}

void CIPServer::socketConnected()
{
    qDebug() << "Connected";
    m_pConnection = nextPendingConnection();
    m_stream.setDevice(m_pConnection);
    connect(m_pConnection, SIGNAL(disconnected()), m_pConnection, SLOT(deleteLater()));
    connect(m_pConnection, SIGNAL(readyRead()), this, SLOT(update()));
}

void CIPServer::update()
{
    if (m_pConnection->bytesAvailable() >= 4) {
        int i;
        m_stream >> i;
        qDebug() << i;
    }
}

main.h:

#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#include <QtCore/QDataStream>
#include <QtCore/QThread>


    /// \brief Creates a socket for inter-process communication
    class CIPSocket
        : public QObject
    {
        Q_OBJECT;

    public:
        /// Constructor
        CIPSocket();
        /// Destructor
        virtual ~CIPSocket();

        /// Send the data
        void update(int i);

    public slots:
        /// Enables updating
        void connectionReady();
        /// Disables updating
        void connectionLost();

    private:
        /// The target stream
        QDataStream m_stream;
        /// The socket connecting to server
        QLocalSocket* m_pSocket;
        /// Indicates if the socket is connected
        bool m_bIsReady;
    };

    /// \brief Creates a server for inter-process communication
    class CIPServer
        : public QLocalServer
    {
        Q_OBJECT;

    public:
        /// Constructor
        CIPServer(QObject* parent = NULL);
        /// Destructor
        virtual ~CIPServer();
        /// Starts the server
        void start();

    private slots:
        /// Connects the socket to the stream and to the update function
        void socketConnected();
        /// Reads the data from the stream and emits a the results
        void update();

    private:
        /// The currently connected socket
        QLocalSocket* m_pConnection;
        /// The incoming stream
        QDataStream m_stream;
    };

demo.pro:

CONFIG += qt debug
QT += network
HEADERS += main.h
SOURCES += main.cpp
CONFIG += console

Solution

  • The error occurs due to the event loop not running. Starting QCoreApplication starts the event loop, but waits for the application to quit. Therefore the sending has to be done in another thread. Attached code shows the correct usage.

    main.cpp:

    #include "main.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication app(argc, argv);
        printf("Server (0) or Socket (1)?\n");
        char c = getchar();
        if (c == '0') {
            CIPServer server;
            QCoreApplication::exec();
        }
        else if (c == '1') {
            CIPSocket socket;
            CSender sender(500);
            QObject::connect(&sender, SIGNAL(sendMessage(int)), &socket, SLOT(update(int)));
            QObject::connect(&sender, SIGNAL(allMessagesSent()), &socket, SLOT(close()));
            sender.start();
            QCoreApplication::exec();
        }
    }
    
    /*--------------------------------------------------------------------------
       CIPSocket
    ----------------------------------------------------------------------------*/
    CIPSocket::CIPSocket()
    : m_bIsReady(false)
    {
        m_pSocket = new QLocalSocket(this);
        m_stream.setDevice(m_pSocket);
    
        connect(m_pSocket, SIGNAL(connected()), this, SLOT(connectionReady()));
        connect(m_pSocket, SIGNAL(disconnected()), this, SLOT(connectionLost()));
    
        m_pSocket->connectToServer("DemoServer");
    }
    
    CIPSocket::~CIPSocket()
    {
        delete m_pSocket;
        m_pSocket = NULL;
    }
    
    void CIPSocket::update(int i)
    {
        if (m_bIsReady)
            m_stream << i;
    }
    
    void CIPSocket::connectionReady()
    { m_bIsReady = true; }
    
    void CIPSocket::connectionLost()
    { m_bIsReady = false; }
    
    void CIPSocket::close()
    { QCoreApplication::exit(); }
    
    /*--------------------------------------------------------------------------
       CIPServer
    ----------------------------------------------------------------------------*/
    CIPServer::CIPServer(QObject* parent)
    : QLocalServer(parent)
    {
        if (!listen("DemoServer")) {
            throw ("Could not connect to 'DemoServer'");
        }
        connect(this, SIGNAL(newConnection()), this, SLOT(socketConnected()));
    }
    
    CIPServer::~CIPServer()
    {}
    
    void CIPServer::socketConnected()
    {
        qDebug() << "Connected";
        m_pConnection = nextPendingConnection();
        m_stream.setDevice(m_pConnection);
        connect(m_pConnection, SIGNAL(disconnected()), m_pConnection, SLOT(deleteLater()));
        connect(m_pConnection, SIGNAL(readyRead()), this, SLOT(update()));
        connect(m_pConnection, SIGNAL(disconnected()), this, SLOT(close()));
    }
    
    void CIPServer::update()
    {
        if (m_pConnection->bytesAvailable() >= 4) {
            int i;
            m_stream >> i;
            qDebug() << i;
        }
    }
    
    void CIPServer::close()
    { QCoreApplication::exit(); }
    
    /*--------------------------------------------------------------------------
       CSender
    ----------------------------------------------------------------------------*/
    CSender::CSender(int iNumMessages)
    : m_iNumMessages(iNumMessages)
    {}
    
    CSender::~CSender()
    {}
    
    void CSender::run()
    {
        while (m_iNumMessages > 0) {
            emit sendMessage(m_iNumMessages);
            msleep(10);
            m_iNumMessages--;
        }
        emit allMessagesSent();
    }
    

    main.h:

    #include <QtNetwork/QLocalServer>
    #include <QtNetwork/QLocalSocket>
    #include <QtCore/QDataStream>
    #include <QtCore/QThread>
    #include <QtCore/QCoreApplication>
    
    
        /// \brief Creates a socket for inter-process communication
        class CIPSocket
            : public QObject
        {
            Q_OBJECT;
    
        public:
            /// Constructor
            CIPSocket();
            /// Destructor
            virtual ~CIPSocket();
    
        public slots:
            /// Enables updating
            void connectionReady();
            /// Disables updating
            void connectionLost();
            /// Send the data
            void update(int i);
            /// Close the application
            void close();
    
        private:
            /// The target stream
            QDataStream m_stream;
            /// The socket connecting to server
            QLocalSocket* m_pSocket;
            /// Indicates if the socket is connected
            bool m_bIsReady;
        };
    
        /// \brief Creates a server for inter-process communication
        class CIPServer
            : public QLocalServer
        {
            Q_OBJECT;
    
        public:
            /// Constructor
            CIPServer(QObject* parent = NULL);
            /// Destructor
            virtual ~CIPServer();
    
        private slots:
            /// Connects the socket to the stream and to the update function
            void socketConnected();
            /// Reads the data from the stream and emits a the results
            void update();
            /// Close the application
            void close();
    
        private:
            /// The currently connected socket
            QLocalSocket* m_pConnection;
            /// The incoming stream
            QDataStream m_stream;
        };
    
        /// \brief Sends the messages via CIPSocket
        class CSender
            : public QThread
        {
            Q_OBJECT;
    
        public:
            /// Constructor
            CSender(int iNumMessages);
            /// Destructor
            virtual ~CSender();
            /// Sends the requestet number of messages in 10 ms steps
            virtual void run();
    
        signals:
            /// Sends the message via the CIPSocket
            void sendMessage(int);
            /// Informs about all messages being sent
            void allMessagesSent();
    
        private:
            /// The number of messages to send
            int m_iNumMessages;
        };