benlau / qsyncable

A solution of nested Json List Model
Apache License 2.0
118 stars 40 forks source link

"QSDiffRunner.compare() - Duplicated or missing key" leads to endless loop. #6

Open ali1234 opened 5 years ago

ali1234 commented 5 years ago

Sometimes my program prints this. It happens very rarely but when it does happen the program freezes and uses 100% CPU forever. It seems to be getting suck inside a loop in the diff runner algorithm. I was able to obtain a backrace:

#0  0x00007fae8b4b1e1f in  () at /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#1  0x00007fae8b4a9279 in QVariant::QVariant(QVariant const&) () at /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#2  0x000056348bb41922 in QMapNode<QString, QVariant>::copy(QMapData<QString, QVariant>*) const ()
#3  0x000056348bb41991 in QMapNode<QString, QVariant>::copy(QMapData<QString, QVariant>*) const ()
#4  0x000056348bb41991 in QMapNode<QString, QVariant>::copy(QMapData<QString, QVariant>*) const ()
#5  0x000056348bb41991 in QMapNode<QString, QVariant>::copy(QMapData<QString, QVariant>*) const ()
#6  0x000056348bb4adee in QMap<QString, QVariant>::detach_helper() ()
#7  0x000056348bb51941 in QSDiffRunnerAlgo::compare(QList<QVariant> const&, QList<QVariant> const&) ()
#8  0x000056348bb404f5 in QSDiffRunner::compare(QList<QVariant> const&, QList<QVariant> const&) ()
#9  0x000056348bb556c0 in QSJsonListModel::sync() ()
#10 0x000056348bb557f3 in QSJsonListModel::setSource(QList<QVariant> const&) ()
#11 0x000056348bbf2f25 in QSJsonListModel::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) ()
#12 0x000056348bbf2fc3 in QSJsonListModel::qt_metacall(QMetaObject::Call, int, void**) ()
#13 0x00007fae8c12478e in QQmlVMEMetaObject::metaCall(QObject*, QMetaObject::Call, int, void**) () at /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#14 0x00007fae8c13b242 in  () at /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#15 0x00007fae8c138d04 in QQmlPropertyPrivate::write(QObject*, QQmlPropertyData const&, QVariant const&, QQmlContextData*, QFlags<QQmlPropertyData::WriteFlag>) () at /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#16 0x00007fae8c1028d9 in QV4::QObjectWrapper::setProperty(QV4::ExecutionEngine*, QObject*, QQmlPropertyData*, QV4::Value const&) () at /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#17 0x00007fae8d2c6540 in  ()
#18 0x0000000000000020 in  ()
#19 0x00032000000000c8 in  ()
#20 0x0000000000000000 in  ()

This is most likely caused by a transient error on the remote server. In any case, I think the algorithm should never get stuck regardless of what input is sent to it.

ali1234 commented 5 years ago

Here is a more complete backtrace:

#0  0x00007f990fd1420c in QVariant::QVariant(QVariant const&) () at /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#1  0x000055a459a4e8ee in QMapData<QString, QVariant>::createNode(QString const&, QVariant const&, QMapNode<QString, QVariant>*, bool) (this=0x55a45b2733d0, k=..., v=..., parent=0x0, left=false)
    at /usr/include/x86_64-linux-gnu/qt5/QtCore/qmap.h:230
#2  0x000055a459a4eb9c in QMapNode<QString, QVariant>::copy(QMapData<QString, QVariant>*) const (this=0x55a45af0ed60, d=0x55a45b2733d0) at /usr/include/x86_64-linux-gnu/qt5/QtCore/qmap.h:258
#3  0x000055a459a4ec40 in QMapNode<QString, QVariant>::copy(QMapData<QString, QVariant>*) const (this=0x55a45af1dc00, d=0x55a45b2733d0) at /usr/include/x86_64-linux-gnu/qt5/QtCore/qmap.h:267
#4  0x000055a459a4ec40 in QMapNode<QString, QVariant>::copy(QMapData<QString, QVariant>*) const (this=0x55a45af0f120, d=0x55a45b2733d0) at /usr/include/x86_64-linux-gnu/qt5/QtCore/qmap.h:267
#5  0x000055a459a4ebe5 in QMapNode<QString, QVariant>::copy(QMapData<QString, QVariant>*) const (this=0x55a45af0ede0, d=0x55a45b2733d0) at /usr/include/x86_64-linux-gnu/qt5/QtCore/qmap.h:261
#6  0x000055a459a55ed5 in QMap<QString, QVariant>::detach_helper() (this=0x7fffb21df440) at /usr/include/x86_64-linux-gnu/qt5/QtCore/qmap.h:998
#7  0x000055a459a5497d in QMap<QString, QVariant>::detach() (this=0x7fffb21df440) at /usr/include/x86_64-linux-gnu/qt5/QtCore/qmap.h:364
#8  0x000055a459a53c12 in QMap<QString, QVariant>::operator[](QString const&) (this=0x7fffb21df440, akey=...) at /usr/include/x86_64-linux-gnu/qt5/QtCore/qmap.h:663
#9  0x000055a459a57d77 in QSDiffRunnerAlgo::compare(QList<QVariant> const&, QList<QVariant> const&) (this=0x7fffb21df3f0, from=..., to=...) at ../lib/qsyncable/qsdiffrunneralgo.cpp:254
#10 0x000055a459a4dee7 in QSDiffRunner::compare(QList<QVariant> const&, QList<QVariant> const&) (this=0x7fffb21df4c0, from=..., to=...) at ../lib/qsyncable/qsdiffrunner.cpp:62
#11 0x000055a459a5c6f3 in QSJsonListModel::sync() (this=0x55a45b02f650) at ../lib/qsyncable/qsjsonlistmodel.cpp:187
#12 0x000055a459a5c5a9 in QSJsonListModel::setSource(QList<QVariant> const&) (this=0x55a45b02f650, source=...) at ../lib/qsyncable/qsjsonlistmodel.cpp:128
#13 0x000055a459aec541 in QSJsonListModel::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) (_o=0x55a45b02f650, _c=QMetaObject::WriteProperty, _id=1, _a=0x7fffb21df6d0) at moc_qsjsonlistmodel.cpp:138
#14 0x000055a459aec705 in QSJsonListModel::qt_metacall(QMetaObject::Call, int, void**) (this=0x55a45b02f650, _c=QMetaObject::WriteProperty, _id=1, _a=0x7fffb21df6d0) at moc_qsjsonlistmodel.cpp:188
#15 0x00007f991098f78e in QQmlVMEMetaObject::metaCall(QObject*, QMetaObject::Call, int, void**) () at /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#16 0x00007f99109a6242 in  () at /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#17 0x00007f99109a3d04 in QQmlPropertyPrivate::write(QObject*, QQmlPropertyData const&, QVariant const&, QQmlContextData*, QFlags<QQmlPropertyData::WriteFlag>) () at /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#18 0x00007f991096d8d9 in QV4::QObjectWrapper::setProperty(QV4::ExecutionEngine*, QObject*, QQmlPropertyData*, QV4::Value const&) () at /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#19 0x00007f9911ca2540 in  ()
#20 0x0000000000000020 in  ()
#21 0x00032000000000c8 in  ()
#22 0x0000000000000000 in  ()
ali1234 commented 5 years ago

I managed to capture the error and reduce it to the minimal case. This will hang QSyncable every time:

import QtQuick 2.2
import QSyncable 1.0

Rectangle {

    JsonListModel {
        id: something
        keyField: "i"
    }

    Component.onCompleted: {
        console.log('x');
        something.source = JSON.parse('[{"i":"a"},{"i":"b"},{"i":"b"}]');
        console.log('y');
        something.source = JSON.parse('[{"i":"b"}]');
        console.log('z');
    }
}

Outputs the following and then hangs forever:

qml: x
qml: y
QSDiffRunner.compare() - Duplicated or missing key.  ' "b" '

I realise that the duplicate key is an error but this is what the API server I have to deal with does. I think QSyncable should not hang forever on bad input.