从Qml调用C ++时Qt 5.12零星的SIGSEGV

时间:2019-01-16 13:35:53

标签: c++ qt qml

我的应用程序内部有一个带有2个状态(连接/断开连接)的Qml按钮。这是各自的代码:

        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;
           }
        }

我看到的是,我不时调用C ++代理的connectToEmulatorService方法时得到了SIGSEGV。这是相应的c ++代码:

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回溯提供以下输出:

(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  ()

这可能是什么原因?如何克服呢? Qt 5.12 Ubuntu 18.10 g ++ 7.3

PS。问题与

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

似乎在functorExecutor线程尝试访问它之前,已删除了临时QML对象映射。但是,如果我将传入的QVariant复制到类成员变量,为什么会发生这种情况?作为一种解决方法,我在调用C ++之前为我的代理声明了一堆Q_PROPERTY,以在其中存储QML数据。这似乎可行。但我相信Qt中存在某个问题。任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:0)

这是Qt-JIRA的官方回复:

“您正在从两个不同的线程访问您的m_connectionParams而不进行锁定。主线程在connectToEmulatorService()中写入它,而工作线程在connectToEmulatorServiceImpl()中对其进行读取。如果在工作线程刚刚读取时主线程再次进行了写入,在之前的迭代中,您会崩溃。这是最明显的问题。

但是,即使您确实锁定了互斥锁来访问m_connectionParams,这仍然是不安全的。您拥有的变体内部有一个QJSValue。为了将其转换为地图,我们必须深入调用JavaScript引擎,构造作用域,并与JavaScript堆进行交互。如果其他JavaScript同时在另一个线程中运行,则您会遇到麻烦。

只需在主线程中提取QVariantMap并将其作为参数传递给函子即可。”

因此,拥有QVariant副本是不够的……它仅位于Qt源码内部。