Qt 5.12 sporadic SIGSEGV when calling C++ from Qml

Viewed 407

There is a Qml Button with 2 states( connect/disconnect ) inside my App. Here is the respective code :

        Button {
           id : connectDisconnectButton
           anchors.centerIn: parent
           property bool isConnected : false
           text : isConnected ? qsTr("Disconnect") : qsTr("Connect")
           antialiasing: true

           property var currentConnectionParams : ({})

           onClicked: {
              if ( isConnected ) {
                 proxy.disconnectFromEmulatorService();
              } else {
                 connectDisconnectButton.currentConnectionParams["port"] = serviceConnectionPort.getValue();
                 connectDisconnectButton.currentConnectionParams["ip_addr"] = String(serviceConnectionIpPart_0.getValue())
                       + String(".") + String(serviceConnectionIpPart_1.getValue())
                 + String(".") + String(serviceConnectionIpPart_2.getValue())
                 + String(".") + String(serviceConnectionIpPart_3.getValue());

                 proxy.connectToEmulatorService( connectDisconnectButton.currentConnectionParams );
              }

              isConnected = !isConnected;
           }
        }

What I see is that from time to time I get SIGSEGV while calling connectToEmulatorService method of my C++ proxy. Here is the respective c++ code :

Q_INVOKABLE void connectToEmulatorService( QVariant in )
{
   m_serviceConnectionParams = in;
   emit connectionInitiatedSignal();
   m_functorExecutor->executeFunctor( boost::bind(&EmulatorControlProxy::connectToEmulatorServiceImpl, this ) );
}

void connectToEmulatorServiceImpl()
{
   QMap<QString, QVariant> connectionParams_ = m_serviceConnectionParams.toMap();
   try {
      emit connectionOkSignal();
   }catch(...) {
      Error("connectToEmulatorServiceImpl failure : ip = %s, port = %s",
            connectionParams_["ip_addr"].toString().toStdString().c_str(),
            connectionParams_["port"].toString().toStdString().c_str() );
      emit connectionFailedSignal();
   }
}

GDB backtrace gives the following output :

(gdb) backtrace
#0  0x00007ffff6e0777b in QV4::QObjectWrapper::virtualGet(QV4::Managed const*, QV4::PropertyKey, QV4::Value const*, bool*) ()
    at /home/developer/Qt5.12.0/5.12.0/gcc_64/lib/libQt5Qml.so.5
#1  0x00007ffff6e8802b in QV4::Runtime::method_loadProperty(QV4::ExecutionEngine*, QV4::Value const&, int) () at /home/developer/Qt5.12.0/5.12.0/gcc_64/lib/libQt5Qml.so.5
#2  0x00007fffe0003d9a in  ()
#3  0x0000000000000000 in  ()

What could be the reason for this? How to overcome this ? Qt 5.12 Ubuntu 18.10 g++ 7.3

PS. The problem is somehow related to

QMap<QString, QVariant> connectionParams_ = m_serviceConnectionParams.toMap();

It seems that temporary QML object map is being deleted before functorExecutor thread tries to access it. But why does it happen, if I copy incoming QVariant to a class member variable? As a workaround I declared a bunch of Q_PROPERTY for my proxy to store QML data there before invoke C++. And that seems to work. But I believe there is an issue somewhere in Qt. Any help is appreciated.

1 Answers

Here is an official response from the Qt-JIRA :

"You're accessing your m_connectionParams from two different threads without locking. The main thread writes it in connectToEmulatorService() and the worker thread reads it in connectToEmulatorServiceImpl(). If the main thread writes again while the worker thread is just reading the previous iteration, you get a crash. That's the most glaringly obvious problem.

However, even if you did lock a mutex for access to m_connectionParams, this would still not be safe. The variant you have there is internally a QJSValue. In order to convert that to a map, we have to call deep into the JavaScript engine, construct scopes, and interact with the JavaScript heap. If any other JavaScript is running at the same time in a different thread, you're in trouble.

Just extract the QVariantMap in the main thread and pass that to your functor as parameter."

So, having a copy of QVariant is not enough...it's somewhere deep inside Qt sources.

Related