Heap Corruption when signalling from C++ to QML
-
I'm new to Qt and I typically following the example codes at Qt Documentation.
I have an Backend C++ application which signals Frontend QML application passing two int variable.
I'm seeing a crash after some time on the signal function showing "heap corruption"Can anyone help me to fix the issue.
// myfunction.pro QT += qml quick widgets core CONFIG += c++11 static QT_QML_DEBUG QMAKE_CXXFLAGS_RELEASE *= -O3
I have around 142 png files embedded into my project. Debug Executable size : 4882 KB
Snippet (c++ file)
#ifndef BUTTON_BACKEND_H #define BUTTON_BACKEND_H #include <QObject> typedef enum { tpKeyReleased = 0, tpKeyTouched, tpKeyPressed }tpKeyStates_t; class ButtonBackend : public QObject { Q_OBJECT Q_PROPERTY(QString tpSide WRITE setTouchPadSide) public: explicit ButtonBackend(QObject *parent = nullptr); ~ButtonBackend(); void tpSide(); void setTouchPadSide(const QString &buttonIndex); signals: void funcButtonChanged(int index, int state); private: int m_key_idx = 0; tpKeyStates_t m_key_state = tpKeyReleased; tpKeyStates_t *m_funcKeyState_prev = nullptr; void decodeFunctionalAreaInfo(QList<QByteArray>); }; #include "buttonbackend.h" ButtonBackend::ButtonBackend(QObject *parent) : QObject(parent) { m_key_idx = 0; if(m_funcKeyState_prev == nullptr) m_funcKeyState_prev = new (nothrow) tpKeyStates_t[3]; } ButtonBackend::~ButtonBackend() {} void ButtonBackend::decodeFunctionalAreaInfo(QList<QByteArray> inputData) { if(3 <= static_cast<unsigned int>(inputData.size()) { m_key_idx = 0; for(QList<QByteArray>::iterator iter = inputData.begin(); iter != inputData.end(); iter++, m_key_idx++) { m_key_state = static_cast<tpKeyStates_t>(atoi(*iter)); if(m_key_state != m_funcKeyState_prev[m_key_idx]) { m_funcKeyState_prev[m_key_idx] = m_key_state; emit funcButtonChanged(m_key_idx, m_key_state); } // emit only if the current key state is changed from previous state } } else { qDebug().nospace() << ERROR_MSG << __func__ << "() Function Key input data count doesn't match (3" << " vs " << inputData.size() << ")"; } } #include <QGuiApplication> #include <QQmlApplicationEngine> #include "src/buttonbackend.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); qmlRegisterType<ButtonBackend>("io.qt.ButtonBackend",1,0,"ButtonBackend"); QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/qml/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); }
Snippet (QML file):
import QtQuick 2.2 import QtQuick.Window 2.1 import QtQuick.Window 2.12 import QtQuick.Controls.Styles 1.4 import QtQuick.Extras 1.4 import QtGraphicalEffects 1.0 import io.qt.ButtonBackend 1.0 Window { id: window visible: true width: 400 height: 400 color: "#000000" flags: Qt.FramelessWindowHint Item { id: backendContainer FuncLayoutTemplate { id:func_layout; ButtonBackend { id:func_BkEnd } Connections { target: func_BkEnd onFuncButtonChanged: { var l_index = index; var l_state = state; console.log(" AF : Function Button Changed ",l_index); func_layout.recv_func_butn_state(l_index,l_state); } } } } } // FuncLayoutTemplate.qml import QtQuick 2.0 import QtGraphicalEffects 1.12 Item{ id:funcTemplate width: parent.width height: parent.height int m_func_scr_idx : -1 int p_buttonState : 0 Repeater { id: myFuncRepeater model: 3 MyButton { onSendButtonIndex: m_func_scr_idx = index;} } onM_func_scr_idxChanged: { if(m_func_scr_idx > -1) { // functional logic here... m_func_scr_idx = -1 } function recv_func_butn_state(l_index, l_state){ if(l_index > -1 && l_index < 3) { myFuncRepeater.itemAt(l_index).p_buttonState = l_state; }else console.log("ERROR : invalid function button index : ",l_index); } // MyButton.qml import QtQuick 2.0 import QtGraphicalEffects 1.12 Rectangle { id: buttonRect border.color: "#000000" border.width: 5 color: "black" anchors.centerIn: parent height: parent.height * 0.5 width: parent.width * 0.5 radius: width * 0.5 property int p_buttonState: 0 signal sendButtonIndex(int index) states: [ State { name: "State1"; when: p_buttonState == 1 PropertyChanges { target: buttonRect; border.color: "#00ffff"; } }, State { name: "State2"; when: p_buttonState == 2 PropertyChanges { target: buttonRect; border.color: "#01ff10";} } ] transitions: [ Transition { from: "State2"; to: "*"; onRunningChanged: { if((!running) sendButtonIndex(index) } } ]
The backend c++ code receives from function button state as string from an other application via tcp socket (Format : "0,0,0") at every 25mSec interval, not matter the key state is changed or not.
The above code snippet is the logic that I'm using to receive the decoded string from backend and send to qml as integer value.
After few minutes of running (I tested in both release mode and debug mode), the program crashes saying "A Heap has been Corrupted" at moc_buttonbackend.cpp after emitting the signal.void ButtonBackend::funcButtonChanged(int _t1, int _t2) { void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))), const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t2))) }; QMetaObject::activate(this, &staticMetaObject, 0, _a); }
I have tested the code commenting
myFuncRepeater.itemAt(l_index).p_buttonState = l_state;
which works fine. Meaning I'm not consuming/using the information passed by the backend. What could be causing the heap to grow once I started consuming.- What happens to
var l_index and var l_state
and its scope? - How does the backend knows the emitted signal is fully consumed?
-Thanks in advance.
- What happens to
-
@Vyuvaraj said in Heap Corruption when signalling from C++ to QML:
Are you sure
l_index
is greater than zero? I don't see a check for that in the code (but I've only had a brief look).what happens to var l_index and var l_state and its scope?
These values are completely independent of your C++ backend. When you emit a signal, POC types (like int) are copied. It does not matter whether values in C++ are still in scope or not.
How does the backend knows the emitted signal is fully consumed?
It has no idea. Emitting a signal is a "fire and forget" kind of action.
-
@Vyuvaraj said in Heap Corruption when signalling from C++ to QML:
function recv_func_butn_state(l_index, l_state){
if(l_index > -1 && l_index < 3) {
myFuncRepeater.itemAt(l_index).p_buttonState = l_state;
}else
console.log("ERROR : invalid function button index : ",l_index);
} -
@Vyuvaraj said in Heap Corruption when signalling from C++ to QML:
Meaning I'm not consuming/using the information passed by the backend. What could be causing the heap to grow once I started consuming.
Heap corruption does not mean that the heap is growing. Instead, it means that data was written to the wrong place in the heap.
- How does the backend knows the emitted signal is fully consumed?
As @sierdzio said: It doesn't know, and it doesn't need to know.
When the signal is emitted, the signal data is passed to the signal handler function. In your case, the signal handler is
onFuncButtonChanged
.The signal handler function runs from start to finish, and then the signal data is freed from memory.
- What happens to
var l_index and var l_state
and its scope?
Nothing happens to them.
This:
Connections { target: func_BkEnd onFuncButtonChanged: { var l_index = index; var l_state = state; console.log(" AF : Function Button Changed ",l_index); func_layout.recv_func_butn_state(l_index,l_state); } }
...is the same as this:
function processButton(idx, st) { var l_index = idx; var l_state = st; console.log(" AF : Function Button Changed ",l_index); func_layout.recv_func_butn_state(l_index,l_state); } Connections { target: func_BkEnd onFuncButtonChanged: processButton(index, state); }
Even if you comment out
myFuncRepeater.itemAt(l_index).p_buttonState = l_state;
, the other code inprocessButton()
still runs.tpKeyStates_t *m_funcKeyState_prev = nullptr;
It's a good idea to convert the above to
QVector<tpKeyStates_t>
.Raw arrays make it easier for you to accidentally corrupt your heap.
if(3 <= static_cast<unsigned int>(inputData.size())
You don't need a
static_cast
here.