Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Very intermittent "QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread"
Forum Updated to NodeBB v4.3 + New Features

Very intermittent "QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread"

Scheduled Pinned Locked Moved Unsolved General and Desktop
8 Posts 2 Posters 838 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    Bob64
    wrote on last edited by
    #1

    I know questions about this message have cropped up at various times but I believe I am already handling things correctly with respect to what usually gives rise to it.

    All reading and writing on the socket is encapsulated in a class that has been moved to a "communications thread" using moveToThread. All messages are then strictly passed via slots and signals from the main thread to/from the communications thread.

    This has worked without issue for a few years, but now I have had a few reports of very intermittent issues that seem to start with a QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread message appearing in my log.

    I am yet to see the issue myself so I have very little to go on.

    The main thing that has changed in recent weeks is a switch from 5.9.6 to 5.15.10.

    I know it's a long shot but posting here in case it rings any bells with anyone. Are there any circumstances, beyond the more fundamental ones that are usually discussed, that could give rise to this message appearing intermittently?

    Christian EhrlicherC 1 Reply Last reply
    0
    • B Bob64

      I know questions about this message have cropped up at various times but I believe I am already handling things correctly with respect to what usually gives rise to it.

      All reading and writing on the socket is encapsulated in a class that has been moved to a "communications thread" using moveToThread. All messages are then strictly passed via slots and signals from the main thread to/from the communications thread.

      This has worked without issue for a few years, but now I have had a few reports of very intermittent issues that seem to start with a QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread message appearing in my log.

      I am yet to see the issue myself so I have very little to go on.

      The main thing that has changed in recent weeks is a switch from 5.9.6 to 5.15.10.

      I know it's a long shot but posting here in case it rings any bells with anyone. Are there any circumstances, beyond the more fundamental ones that are usually discussed, that could give rise to this message appearing intermittently?

      Christian EhrlicherC Offline
      Christian EhrlicherC Offline
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @Bob64 is it a member variable? If so - has it a proper parent?

      Please show the code.

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      B 1 Reply Last reply
      0
      • Christian EhrlicherC Christian Ehrlicher

        @Bob64 is it a member variable? If so - has it a proper parent?

        Please show the code.

        B Offline
        B Offline
        Bob64
        wrote on last edited by
        #3

        @Christian-Ehrlicher Hopefully the below will show the essence of it. It's basically a "JSON-RPC" like mechanism that the broader application uses. I am not sure exactly what you meant by it when you asked about parents but you will see that the JsonServer object that is moved to the comms thread is not given a parent when it is constructed. Should it be?

        Something to note is that, even in the cases where the issue arises, this object has already been used to make numerous remote calls without any errors before the issue happens.

        class Rpc : public QObject
        {
            Q_OBJECT
        public:
            Rpc(QObject* parent = nullptr);
            ~Rpc();
        
            void callMethodAsync(const QString& name, const QVariantMap& arguments);
            QVariant callMethodSync(const QString& name, const QVariantMap& arguments);
        
        signals:
            void asyncResponse(QVariant response);
            //...
            void remoteMethodRequest(QByteArray methodInfo);
        
        private slots:
            void receiveResponseAsync(QByteArray response);
            //...
            void stopCommsThread();
        
        private:
            QThread m_commsThread;
            JsonServer* m_jsonServer;
        };
        
        Rpc::Rpc(QObject* parent)
            : QObject(parent)
        {
            m_jsonServer = new JsonServer();
            m_jsonServer->moveToThread(&m_commsThread);
            connect(&m_commsThread, &QThread::finished, m_jsonServer, &QObject::deleteLater);
        
            //...
            connect(this, &Rpc::remoteMethodRequest, m_jsonServer, &JsonServer::remoteMethodCall);
        
            connect(m_jsonServer, &JsonServer::methodResponse, this, &EngineRpc::receiveResponseAsync);
            //...
            m_commsThread.start();
        }
        
        void Rpc::callMethodAsync(const QString& name, const QVariantMap& arguments)
        {
            QByteArray jsonRequest = jsonEncodeMethodCall(name, arguments);
            emit remoteMethodRequest(jsonRequest);
        }
        
        void Rpc::receiveResponseAsync(QByteArray response)
        {
            emit asyncResponse(jsonDecodeMessage(response));
        }
        

        By the way, the reason I am using a thread is that this turned out to be the most straightforward way to support a "synchronous method call" on this interface as well as the asynchronous call shown here. If I hadn't had a need for that, I could probably have avoided threading.

        Christian EhrlicherC 1 Reply Last reply
        0
        • B Bob64

          @Christian-Ehrlicher Hopefully the below will show the essence of it. It's basically a "JSON-RPC" like mechanism that the broader application uses. I am not sure exactly what you meant by it when you asked about parents but you will see that the JsonServer object that is moved to the comms thread is not given a parent when it is constructed. Should it be?

          Something to note is that, even in the cases where the issue arises, this object has already been used to make numerous remote calls without any errors before the issue happens.

          class Rpc : public QObject
          {
              Q_OBJECT
          public:
              Rpc(QObject* parent = nullptr);
              ~Rpc();
          
              void callMethodAsync(const QString& name, const QVariantMap& arguments);
              QVariant callMethodSync(const QString& name, const QVariantMap& arguments);
          
          signals:
              void asyncResponse(QVariant response);
              //...
              void remoteMethodRequest(QByteArray methodInfo);
          
          private slots:
              void receiveResponseAsync(QByteArray response);
              //...
              void stopCommsThread();
          
          private:
              QThread m_commsThread;
              JsonServer* m_jsonServer;
          };
          
          Rpc::Rpc(QObject* parent)
              : QObject(parent)
          {
              m_jsonServer = new JsonServer();
              m_jsonServer->moveToThread(&m_commsThread);
              connect(&m_commsThread, &QThread::finished, m_jsonServer, &QObject::deleteLater);
          
              //...
              connect(this, &Rpc::remoteMethodRequest, m_jsonServer, &JsonServer::remoteMethodCall);
          
              connect(m_jsonServer, &JsonServer::methodResponse, this, &EngineRpc::receiveResponseAsync);
              //...
              m_commsThread.start();
          }
          
          void Rpc::callMethodAsync(const QString& name, const QVariantMap& arguments)
          {
              QByteArray jsonRequest = jsonEncodeMethodCall(name, arguments);
              emit remoteMethodRequest(jsonRequest);
          }
          
          void Rpc::receiveResponseAsync(QByteArray response)
          {
              emit asyncResponse(jsonDecodeMessage(response));
          }
          

          By the way, the reason I am using a thread is that this turned out to be the most straightforward way to support a "synchronous method call" on this interface as well as the asynchronous call shown here. If I hadn't had a need for that, I could probably have avoided threading.

          Christian EhrlicherC Offline
          Christian EhrlicherC Offline
          Christian Ehrlicher
          Lifetime Qt Champion
          wrote on last edited by
          #4

          What is jsonsocket and where do you create the real socket? Is it a proper member of JSonSocket so it gets moved too?

          Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
          Visit the Qt Academy at https://academy.qt.io/catalog

          B 1 Reply Last reply
          0
          • Christian EhrlicherC Christian Ehrlicher

            What is jsonsocket and where do you create the real socket? Is it a proper member of JSonSocket so it gets moved too?

            B Offline
            B Offline
            Bob64
            wrote on last edited by Bob64
            #5

            @Christian-Ehrlicher JsonServer is a layer on top of the actual socket and is the owner of the socket. This is a very stripped down version but hopefully is enough to give the idea of it and clarify the ownership question. (I haven't shown the details of the SocketWriter member but it's just a small helper that encapsulates the bytesWritten signal handling of QTcpSocket.)

            class JsonServer : public QObject
            {
                Q_OBJECT
            public:
                JsonServer();
            
            public slots:
                void remoteMethodCall(QByteArray methodCallInfo);
            
            signals:
                void methodResponse(QByteArray response);
            
            private slots:
                void readBytes();
            
            private:
                bool m_isConnected;
                QByteArray m_buffer;
                QTcpSocket m_server;
                SocketWriter m_writer;
            };
            
            JsonServer::JsonServer()
                : m_isConnected(false),
                  m_server(this),
                  m_writer(m_server)
            {
                // various connection handling, error handling, message processing details
                // excluded....
            
                connect(&m_server, &QTcpSocket::readyRead, this, &JsonServer::readBytes);
            }
            
            void JsonServer::remoteMethodCall(QByteArray methodCallInfo)
            {
                m_writer.write(methodCallInfo);
            }
            
            void JsonServer::readBytes()
            {
                auto bytes = m_server.readAll();
                m_buffer += bytes;
            
                // details omitted but essentially
                //    emit methodResponse(response); 
                //  once a complete response is available in m_buffer
            }
            Christian EhrlicherC 1 Reply Last reply
            0
            • B Bob64

              @Christian-Ehrlicher JsonServer is a layer on top of the actual socket and is the owner of the socket. This is a very stripped down version but hopefully is enough to give the idea of it and clarify the ownership question. (I haven't shown the details of the SocketWriter member but it's just a small helper that encapsulates the bytesWritten signal handling of QTcpSocket.)

              class JsonServer : public QObject
              {
                  Q_OBJECT
              public:
                  JsonServer();
              
              public slots:
                  void remoteMethodCall(QByteArray methodCallInfo);
              
              signals:
                  void methodResponse(QByteArray response);
              
              private slots:
                  void readBytes();
              
              private:
                  bool m_isConnected;
                  QByteArray m_buffer;
                  QTcpSocket m_server;
                  SocketWriter m_writer;
              };
              
              JsonServer::JsonServer()
                  : m_isConnected(false),
                    m_server(this),
                    m_writer(m_server)
              {
                  // various connection handling, error handling, message processing details
                  // excluded....
              
                  connect(&m_server, &QTcpSocket::readyRead, this, &JsonServer::readBytes);
              }
              
              void JsonServer::remoteMethodCall(QByteArray methodCallInfo)
              {
                  m_writer.write(methodCallInfo);
              }
              
              void JsonServer::readBytes()
              {
                  auto bytes = m_server.readAll();
                  m_buffer += bytes;
              
                  // details omitted but essentially
                  //    emit methodResponse(response); 
                  //  once a complete response is available in m_buffer
              }
              Christian EhrlicherC Offline
              Christian EhrlicherC Offline
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on last edited by Christian Ehrlicher
              #6

              This looks all good. Where do you open the tcp server? It should be done in the new thread. Maybe also connect to QThread::started() and open or even better create the tcp server in there just to be sure. I'm not yet sure if there was a problem with an internal member of QTcpServer not properly parented so it was not moved... but can't find a bug report about this.

              /edit: maybe also run with QT_FATAL_WARNINGS to see where exactly the warning comes from

              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
              Visit the Qt Academy at https://academy.qt.io/catalog

              B 1 Reply Last reply
              0
              • Christian EhrlicherC Christian Ehrlicher

                This looks all good. Where do you open the tcp server? It should be done in the new thread. Maybe also connect to QThread::started() and open or even better create the tcp server in there just to be sure. I'm not yet sure if there was a problem with an internal member of QTcpServer not properly parented so it was not moved... but can't find a bug report about this.

                /edit: maybe also run with QT_FATAL_WARNINGS to see where exactly the warning comes from

                B Offline
                B Offline
                Bob64
                wrote on last edited by
                #7

                @Christian-Ehrlicher :

                This looks all good. Where do you open the tcp server? It should be done in the new thread.
                Maybe also connect to QThread::started() and open or even better create the tcp server in there just to be sure. I'm not yet sure if there was a problem with an internal member of QTcpServer not properly parented so it was not moved... but can't find a bug report about this.

                Apologies - my naming might be a bit confusing. Just to be clear, it's a QTcpSocket at this end and the server is actually the remote thing I am talking to. I'm using "server" in my names here as I am thinking of it as the interface to the server.

                I do have a connectToHost call in the class though, and that is definitely called some time after the moveToThread call.

                Perhaps, along the lines of your suggestion, I should try deferring the construction of the QTcpSocket until the thread has started?

                /edit: maybe also run with QT_FATAL_WARNINGS to see where exactly the warning comes from

                Thank you - I will try that.

                Christian EhrlicherC 1 Reply Last reply
                0
                • B Bob64

                  @Christian-Ehrlicher :

                  This looks all good. Where do you open the tcp server? It should be done in the new thread.
                  Maybe also connect to QThread::started() and open or even better create the tcp server in there just to be sure. I'm not yet sure if there was a problem with an internal member of QTcpServer not properly parented so it was not moved... but can't find a bug report about this.

                  Apologies - my naming might be a bit confusing. Just to be clear, it's a QTcpSocket at this end and the server is actually the remote thing I am talking to. I'm using "server" in my names here as I am thinking of it as the interface to the server.

                  I do have a connectToHost call in the class though, and that is definitely called some time after the moveToThread call.

                  Perhaps, along the lines of your suggestion, I should try deferring the construction of the QTcpSocket until the thread has started?

                  /edit: maybe also run with QT_FATAL_WARNINGS to see where exactly the warning comes from

                  Thank you - I will try that.

                  Christian EhrlicherC Offline
                  Christian EhrlicherC Offline
                  Christian Ehrlicher
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  @Bob64 said in Very intermittent "QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread":

                  I should try deferring the construction of the QTcpSocket until the thread has started

                  Yes. Or try to reproduce it in a small example. Does not look that hard 🙂

                  Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                  Visit the Qt Academy at https://academy.qt.io/catalog

                  1 Reply Last reply
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved