About the position of engine->rootContext()->setContextProperty() and engine->load() resulting SEGFAULT
-
I've recently implemented an instrument cluster (since here I will mention it as IC). Meanwhile, something weird happens. The IC has methods named registerModel(), and loadQml(). When I do the two methods separately, it crashes with segmentation fault at the line of _engine->load(url). But when I do these methods in a single method, it works. What's going on?
What I've done is just move one method to another by integrating two functions to resolve this issue. Nothing else has changed. Here is my code. Which part could bring the result of segmentation fault? I am so confused.
Thank you all in advance for the replies!
Main.cpp
#include "InstrumentCluster.h" #include <QDebug> #include <iostream> int main(int argc, char *argv[]) { int appExit = EXIT_FAILURE; try { InstrumentCluster cluster(argc, argv); if (!cluster.openGateway("can1")) { qCritical() << "Failed to open can1"; return EXIT_FAILURE; } ViewModel model; cluster.connectModel("can1", model); /* TWO SEPARATE METHODS: NOT WORKING. PRINT SEGMENTATION FAULT */ // cluster.registerModel("ViewModel", model); // cluster.loadQML("qrc:/Main.qml"); /* INTEGRATED A SINGLE METHOD: WORKING! */ cluster.loadQML("qrc:/Main.qml", "ViewModel", model); appExit = cluster.run(); } catch (const std::exception& e) { qCritical() << "Application error:" << e.what(); } return appExit; }
InstrumentCluster.cpp
#include "InstrumentCluster.h" #include <QQmlContext> #include <QFile> #include <QDebug> /* CON & DESTRUCTOR */ InstrumentCluster::InstrumentCluster(int argc, char** argv) : _app(std::make_unique<QGuiApplication>(argc, argv)), _engine(std::make_unique<QQmlApplicationEngine>()) { } InstrumentCluster::~InstrumentCluster() { for (const auto& pair : _threads) { closeGateway(pair.first); } } /* METHODS */ bool InstrumentCluster::openGateway(const std::string& ifname) { try { if (_findGateway(ifname)) { qWarning() << "[IC] Gateway for" << QString::fromStdString(ifname) << "already exists"; return true; } // Instanciate gateway _gateways.emplace_back(new CANGateway(ifname)); // Instanciate thread and move the gateway worker to thread _threads[ifname] = QThread_ptr(new QThread); _gateways.back()->moveToThread(_threads[ifname].get()); // Set signals _openGatewaySetSignals(_gateways.back(), _threads[ifname], ifname); // Let the worker begin :) _threads[ifname]->start(); qInfo() << "[IC] CAN Gateway opened for interface:" << QString::fromStdString(ifname); return true; } catch (const std::exception& e) { qCritical() << "[IC] Failed to open gateway for" << QString::fromStdString(ifname) << ":" << e.what(); auto it = _threads.find(ifname); if (it != _threads.end()) { _threads.erase(it); } return false; } } void InstrumentCluster::_openGatewaySetSignals(InstrumentCluster::CANGateway_ptr& gateway, InstrumentCluster::QThread_ptr& thread, const std::string& ifname) { // Connect thread management signals QObject::connect(thread.get(), &QThread::started, gateway.get(), &CANGateway::start); QObject::connect(gateway.get(), &CANGateway::finished, thread.get(), &QThread::quit); // Connect status signals QObject::connect(gateway.get(), &CANGateway::connected, [ifname]() { qInfo() << "[IC] CAN Gateway activated:" << QString::fromStdString(ifname); }); QObject::connect(gateway.get(), &CANGateway::disconnected, [ifname]() { qInfo() << "[IC] CAN Gateway deactivated:" << QString::fromStdString(ifname); }); // Connect error handling QObject::connect(gateway.get(), &CANGateway::error, [ifname](const QString& error) { qCritical() << "[IC] CAN Gateway error on" << QString::fromStdString(ifname) << ":" << error; }); } void InstrumentCluster::closeGateway(const std::string& ifname) { const auto& gateway = _findGateway(ifname); if (!gateway) { qWarning() << "[IC] No gateway found for CAN interface:" << QString::fromStdString(ifname); return; } // Stop the gateway gateway->stop(); // Handle thread cleanup auto it = _threads.find(ifname); if (it != _threads.end()) { const auto& thread = it->second; if (thread->isRunning()) { thread->quit(); thread->wait(CLOSE_WAIT); if (thread->isRunning()) { qWarning() << "[IC] Thread for" << QString::fromStdString(ifname) << "did not stop gracefully, terminating..."; thread->terminate(); thread->wait(FORCE_WAIT); } } _threads.erase(it); } _gateways.remove_if([&ifname](const CANGateway_ptr& ptr) { return ptr && ptr->ifname == ifname; }); qInfo() << "[IC] CAN Gateway closed for interface:" << QString::fromStdString(ifname); } /* FUTURE: Implement a method to check if a gateway is connected */ // bool InstrumentCluster::isGatewayConnected(const std::string& interface) const { // const auto& gateway = _findGateway(interface); // return gateway && gateway->isConnected(); // } // FUTURE: Implement a method to list all connected gateways // std::vector<std::string> InstrumentCluster::getConnectedGateways() const { // std::vector<std::string> connectedGateways; // for (const auto& gateway : _gateways) { // if (gateway && gateway->isConnected()) { // connectedGateways.push_back(gateway->interfaceName()); // } // } // return connectedGateways; // } void InstrumentCluster::connectModel(const std::string& interface, ViewModel& model) { const auto& gateway = _findGateway(interface); if (!gateway) { qWarning() << "No gateway found for CAN interface:" << QString::fromStdString(interface); return; } // Connect the single newData signal to ViewModel's receiveData slot QObject::connect(gateway.get(), &CANGateway::newData, &model, &ViewModel::receiveData, Qt::QueuedConnection); qInfo() << "[IC] Connected CAN Gateway" << QString::fromStdString(interface) << "to ViewModel."; } void InstrumentCluster::registerModel(const std::string& name, ViewModel& model) { _engine->rootContext()->setContextProperty(QString::fromStdString(name), &model); qInfo() << "[IC] ViewModel registered as:" << QString::fromStdString(name); } // void InstrumentCluster::loadQML(const std::string& qmlEntry) { void InstrumentCluster::loadQML(const std::string& qmlEntry, const std::string& name, ViewModel& model) { /* ONLY THE DIFFERENCE IS THAT FUNCTION CALL IS MOVED TO HERE FROM registerModel() */ _engine->rootContext()->setContextProperty(QString::fromStdString(name), &model); const QUrl url(QString::fromStdString(qmlEntry)); if (!url.isValid() || url.isEmpty()) { qCritical() << "[IC] QML entry path is invalid:" << QString::fromStdString(qmlEntry); return; } if (url.isLocalFile()) { if (!QFile::exists(url.toLocalFile())) { qCritical() << "[IC] QML file does not exist at path:" << url.toLocalFile(); return; } } QObject::connect(_engine.get(), &QQmlApplicationEngine::objectCreated, _app.get(), [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(EXIT_FAILURE); }, Qt::QueuedConnection); _engine->load(url); qInfo() << "[IC] QML file loaded:" << QString::fromStdString(qmlEntry); } int InstrumentCluster::run() { return _app->exec(); } const InstrumentCluster::CANGateway_ptr& InstrumentCluster::_findGateway(const std::string& interface) const { for (const auto& gateway : _gateways) { if (gateway && gateway->ifname == interface) { return gateway; } } // Return a null reference - this will be checked by the caller static CANGateway_ptr nullPtr; return nullPtr; }
ViewModel.hpp
#ifndef VIEWMODEL_H # define VIEWMODEL_H # include <QObject> # include <QTimer> # include <QByteArray> # define WHEEL_DIAMETER 6.8 // CM # define PI 3.1415 enum canID_e { ID_RPM = 0x10, }; class ViewModel : public QObject { /* QT FRAMEWORK */ Q_OBJECT Q_PROPERTY(int speed READ speed NOTIFY updateSpeed) public slots: void receiveData(int, const QByteArray&); signals: void updateSpeed(); /* CLASS */ public: explicit ViewModel(QObject* parent = nullptr); ~ViewModel(); int speed() const { return _speed; } private: int _speed; // Centimeters per second int _int(const QByteArray&, int) const; // float _float(const QByteArray&, size_t) const; // bool _bool(const QByteArray&, size_t) const; }; #endif
ViewModel.cpp
#include "ViewModel.h" #include <QDebug> #include <cstring> ViewModel::ViewModel(QObject *parent) : QObject(parent), _speed(0) { std::cout << "constructing viewModel\\n"; } ViewModel::~ViewModel() { std::cout << "constructing viewModel\\n"; } /* QT METHODS */ void ViewModel::receiveData(int canID, const QByteArray& data) { qDebug() << "[ViewModel] Received CAN frame - ID:" << Qt::hex << canID << "Data:" << data.toHex(' ') << "Size:" << data.size(); switch (canID) { case ID_RPM: { if (data.size() < 2) { qWarning() << "[ViewModel] RPM frame too short:" << data.size(); break; } // Convert RPM to speed using wheel diameter // Speed = (wheel circumference * RPM) / 60 (to get per second) // Result in cm/s as per your original implementation int val = static_cast<int>(WHEEL_DIAMETER * PI * static_cast<float>(_int(data, 0)) / 60.0f); if (val != _speed) { _speed = val; emit updateSpeed(); } break; } default: qDebug() << "[ViewModel] Unreserved CAN ID received:" << Qt::hex << canID; } } /* CLASS METHODS */ int ViewModel::_int(const QByteArray& data, int pos = 0) const { if (pos + 1 >= data.size()) { return 0; } // Parse as big-endian 16-bit integer return (static_cast<unsigned char>(data[pos]) << 8) | static_cast<unsigned char>(data[pos + 1]); }
-
I've recently implemented an instrument cluster (since here I will mention it as IC). Meanwhile, something weird happens. The IC has methods named registerModel(), and loadQml(). When I do the two methods separately, it crashes with segmentation fault at the line of _engine->load(url). But when I do these methods in a single method, it works. What's going on?
What I've done is just move one method to another by integrating two functions to resolve this issue. Nothing else has changed. Here is my code. Which part could bring the result of segmentation fault? I am so confused.
Thank you all in advance for the replies!
Main.cpp
#include "InstrumentCluster.h" #include <QDebug> #include <iostream> int main(int argc, char *argv[]) { int appExit = EXIT_FAILURE; try { InstrumentCluster cluster(argc, argv); if (!cluster.openGateway("can1")) { qCritical() << "Failed to open can1"; return EXIT_FAILURE; } ViewModel model; cluster.connectModel("can1", model); /* TWO SEPARATE METHODS: NOT WORKING. PRINT SEGMENTATION FAULT */ // cluster.registerModel("ViewModel", model); // cluster.loadQML("qrc:/Main.qml"); /* INTEGRATED A SINGLE METHOD: WORKING! */ cluster.loadQML("qrc:/Main.qml", "ViewModel", model); appExit = cluster.run(); } catch (const std::exception& e) { qCritical() << "Application error:" << e.what(); } return appExit; }
InstrumentCluster.cpp
#include "InstrumentCluster.h" #include <QQmlContext> #include <QFile> #include <QDebug> /* CON & DESTRUCTOR */ InstrumentCluster::InstrumentCluster(int argc, char** argv) : _app(std::make_unique<QGuiApplication>(argc, argv)), _engine(std::make_unique<QQmlApplicationEngine>()) { } InstrumentCluster::~InstrumentCluster() { for (const auto& pair : _threads) { closeGateway(pair.first); } } /* METHODS */ bool InstrumentCluster::openGateway(const std::string& ifname) { try { if (_findGateway(ifname)) { qWarning() << "[IC] Gateway for" << QString::fromStdString(ifname) << "already exists"; return true; } // Instanciate gateway _gateways.emplace_back(new CANGateway(ifname)); // Instanciate thread and move the gateway worker to thread _threads[ifname] = QThread_ptr(new QThread); _gateways.back()->moveToThread(_threads[ifname].get()); // Set signals _openGatewaySetSignals(_gateways.back(), _threads[ifname], ifname); // Let the worker begin :) _threads[ifname]->start(); qInfo() << "[IC] CAN Gateway opened for interface:" << QString::fromStdString(ifname); return true; } catch (const std::exception& e) { qCritical() << "[IC] Failed to open gateway for" << QString::fromStdString(ifname) << ":" << e.what(); auto it = _threads.find(ifname); if (it != _threads.end()) { _threads.erase(it); } return false; } } void InstrumentCluster::_openGatewaySetSignals(InstrumentCluster::CANGateway_ptr& gateway, InstrumentCluster::QThread_ptr& thread, const std::string& ifname) { // Connect thread management signals QObject::connect(thread.get(), &QThread::started, gateway.get(), &CANGateway::start); QObject::connect(gateway.get(), &CANGateway::finished, thread.get(), &QThread::quit); // Connect status signals QObject::connect(gateway.get(), &CANGateway::connected, [ifname]() { qInfo() << "[IC] CAN Gateway activated:" << QString::fromStdString(ifname); }); QObject::connect(gateway.get(), &CANGateway::disconnected, [ifname]() { qInfo() << "[IC] CAN Gateway deactivated:" << QString::fromStdString(ifname); }); // Connect error handling QObject::connect(gateway.get(), &CANGateway::error, [ifname](const QString& error) { qCritical() << "[IC] CAN Gateway error on" << QString::fromStdString(ifname) << ":" << error; }); } void InstrumentCluster::closeGateway(const std::string& ifname) { const auto& gateway = _findGateway(ifname); if (!gateway) { qWarning() << "[IC] No gateway found for CAN interface:" << QString::fromStdString(ifname); return; } // Stop the gateway gateway->stop(); // Handle thread cleanup auto it = _threads.find(ifname); if (it != _threads.end()) { const auto& thread = it->second; if (thread->isRunning()) { thread->quit(); thread->wait(CLOSE_WAIT); if (thread->isRunning()) { qWarning() << "[IC] Thread for" << QString::fromStdString(ifname) << "did not stop gracefully, terminating..."; thread->terminate(); thread->wait(FORCE_WAIT); } } _threads.erase(it); } _gateways.remove_if([&ifname](const CANGateway_ptr& ptr) { return ptr && ptr->ifname == ifname; }); qInfo() << "[IC] CAN Gateway closed for interface:" << QString::fromStdString(ifname); } /* FUTURE: Implement a method to check if a gateway is connected */ // bool InstrumentCluster::isGatewayConnected(const std::string& interface) const { // const auto& gateway = _findGateway(interface); // return gateway && gateway->isConnected(); // } // FUTURE: Implement a method to list all connected gateways // std::vector<std::string> InstrumentCluster::getConnectedGateways() const { // std::vector<std::string> connectedGateways; // for (const auto& gateway : _gateways) { // if (gateway && gateway->isConnected()) { // connectedGateways.push_back(gateway->interfaceName()); // } // } // return connectedGateways; // } void InstrumentCluster::connectModel(const std::string& interface, ViewModel& model) { const auto& gateway = _findGateway(interface); if (!gateway) { qWarning() << "No gateway found for CAN interface:" << QString::fromStdString(interface); return; } // Connect the single newData signal to ViewModel's receiveData slot QObject::connect(gateway.get(), &CANGateway::newData, &model, &ViewModel::receiveData, Qt::QueuedConnection); qInfo() << "[IC] Connected CAN Gateway" << QString::fromStdString(interface) << "to ViewModel."; } void InstrumentCluster::registerModel(const std::string& name, ViewModel& model) { _engine->rootContext()->setContextProperty(QString::fromStdString(name), &model); qInfo() << "[IC] ViewModel registered as:" << QString::fromStdString(name); } // void InstrumentCluster::loadQML(const std::string& qmlEntry) { void InstrumentCluster::loadQML(const std::string& qmlEntry, const std::string& name, ViewModel& model) { /* ONLY THE DIFFERENCE IS THAT FUNCTION CALL IS MOVED TO HERE FROM registerModel() */ _engine->rootContext()->setContextProperty(QString::fromStdString(name), &model); const QUrl url(QString::fromStdString(qmlEntry)); if (!url.isValid() || url.isEmpty()) { qCritical() << "[IC] QML entry path is invalid:" << QString::fromStdString(qmlEntry); return; } if (url.isLocalFile()) { if (!QFile::exists(url.toLocalFile())) { qCritical() << "[IC] QML file does not exist at path:" << url.toLocalFile(); return; } } QObject::connect(_engine.get(), &QQmlApplicationEngine::objectCreated, _app.get(), [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(EXIT_FAILURE); }, Qt::QueuedConnection); _engine->load(url); qInfo() << "[IC] QML file loaded:" << QString::fromStdString(qmlEntry); } int InstrumentCluster::run() { return _app->exec(); } const InstrumentCluster::CANGateway_ptr& InstrumentCluster::_findGateway(const std::string& interface) const { for (const auto& gateway : _gateways) { if (gateway && gateway->ifname == interface) { return gateway; } } // Return a null reference - this will be checked by the caller static CANGateway_ptr nullPtr; return nullPtr; }
ViewModel.hpp
#ifndef VIEWMODEL_H # define VIEWMODEL_H # include <QObject> # include <QTimer> # include <QByteArray> # define WHEEL_DIAMETER 6.8 // CM # define PI 3.1415 enum canID_e { ID_RPM = 0x10, }; class ViewModel : public QObject { /* QT FRAMEWORK */ Q_OBJECT Q_PROPERTY(int speed READ speed NOTIFY updateSpeed) public slots: void receiveData(int, const QByteArray&); signals: void updateSpeed(); /* CLASS */ public: explicit ViewModel(QObject* parent = nullptr); ~ViewModel(); int speed() const { return _speed; } private: int _speed; // Centimeters per second int _int(const QByteArray&, int) const; // float _float(const QByteArray&, size_t) const; // bool _bool(const QByteArray&, size_t) const; }; #endif
ViewModel.cpp
#include "ViewModel.h" #include <QDebug> #include <cstring> ViewModel::ViewModel(QObject *parent) : QObject(parent), _speed(0) { std::cout << "constructing viewModel\\n"; } ViewModel::~ViewModel() { std::cout << "constructing viewModel\\n"; } /* QT METHODS */ void ViewModel::receiveData(int canID, const QByteArray& data) { qDebug() << "[ViewModel] Received CAN frame - ID:" << Qt::hex << canID << "Data:" << data.toHex(' ') << "Size:" << data.size(); switch (canID) { case ID_RPM: { if (data.size() < 2) { qWarning() << "[ViewModel] RPM frame too short:" << data.size(); break; } // Convert RPM to speed using wheel diameter // Speed = (wheel circumference * RPM) / 60 (to get per second) // Result in cm/s as per your original implementation int val = static_cast<int>(WHEEL_DIAMETER * PI * static_cast<float>(_int(data, 0)) / 60.0f); if (val != _speed) { _speed = val; emit updateSpeed(); } break; } default: qDebug() << "[ViewModel] Unreserved CAN ID received:" << Qt::hex << canID; } } /* CLASS METHODS */ int ViewModel::_int(const QByteArray& data, int pos = 0) const { if (pos + 1 >= data.size()) { return 0; } // Parse as big-endian 16-bit integer return (static_cast<unsigned char>(data[pos]) << 8) | static_cast<unsigned char>(data[pos + 1]); }
Hi, and welcome!
@Morty56 said in About the position of engine->rootContext()->setContextProperty() and engine->load() resulting SEGFAULT:
What I've done is just move one method to another by integrating two functions to resolve this issue. Nothing else has changed. Here is my code. Which part could bring the result of segmentation fault? I am so confused.
Your code could contain a race condition, where moving functions around changes the outcome of the race. Check your code using tools like ASan and TSan.