【问题标题】:Qt 5.12 sporadic SIGSEGV when calling C++ from Qml从 Qml 调用 C++ 时 Qt 5.12 零星的 SIGSEGV
【发布时间】:2019-01-16 13:35:52
【问题描述】:

我的应用程序中有一个具有 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 复制到类成员变量中,为什么会发生这种情况?作为一种解决方法,我为我的代理声明了一堆 Q_PROPERTY,以便在调用 C++ 之前将 QML 数据存储在那里。这似乎奏效了。但我相信 Qt 的某个地方存在问题。任何帮助表示赞赏。

【问题讨论】:

  • 什么是in?这是connectToEmulatorServiceQVariant 参数的名称吗?
  • 是的,很抱歉造成混淆。刚刚编辑了我的消息

标签: c++ qt qml


【解决方案1】:

这是来自 Qt-JIRA 的官方回复:

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

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

只需在主线程中提取 QVariantMap 并将其作为参数传递给您的仿函数。”

因此,拥有 QVariant 的副本是不够的……它在 Qt 源代码的深处。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-25
    相关资源
    最近更新 更多