3

I'm using QAbstractListModel to expose data to QML ListView. QML SectionScroller is used beside this, which uses get and data functions.

After some time of scrolling, a crash is experienced. The backtrace is:

Program received signal SIGILL, Illegal instruction.
0x0000cdcc in QBasicAtomicInt::ref (
    this=0x35)
    at /usr/include/QtCore/qatomic_armv6.h:119
119 /usr/include/QtCore/qatomic_armv6.h: No such file or directory.
    in /usr/include/QtCore/qatomic_armv6.h
(gdb) bt
#0  0x0000cdcc in QBasicAtomicInt::ref (
    this=0x35)
    at /usr/include/QtCore/qatomic_armv6.h:119
#1  0x0000f4e8 in QString (
    this=0xbebf1a5c, other=...)
    at /usr/include/QtCore/qstring.h:729
#2  [address] in IrregularVerb::getForm0
    (this=0x92e428) at IrregularVerb.h:16
#3  0x0000e29c in IrregularListWrapper::data (this=0x92dd20, index=..., role=33)
    at IrregularListWrapper.cpp:37
#4  0x4010e9c6 in ?? ()
   from /usr/lib/libQtDeclarative.so.4
#5  0x4010e9c6 in ?? ()
   from /usr/lib/libQtDeclarative.so.4
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

On other device (first is N900, the second N950) the backtrace is similar. It's SIGSEGV instead of SIGILL, but the backtrace is identical beside this. I noticed once a situtation where some of the fields have gotten blank at the moment on crash. (the ones using getForm0 and getForm1)

When I added an extra assignement, to the returned value, the crash happened at the assignment.

Some important code:

The element:

class IrregularVerb : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString form0 READ getForm0 CONSTANT)
    Q_PROPERTY(QString form1 READ getForm1 CONSTANT)
    Q_PROPERTY(QString form2 READ getForm2 CONSTANT)
public:
    QString forms[3];
    QString getForm0() const { return forms[0]; }
    QString getForm1() const { return forms[1]; }
    QString getForm2() const { return forms[2]; }
    IrregularVerb(QString a, QString b, QString c) { forms[0] = a; forms[1] = b; forms[2] = c; }
};

The model:

class IrregularListWrapper : public QAbstractListModel
{
    Q_OBJECT
    Q_PROPERTY(QString langName READ getLangName NOTIFY langChanged)
    Q_PROPERTY(int count READ rowCount NOTIFY langChanged)
    Q_ENUMS(Language)
public:

    Q_INVOKABLE int rowCount(const QModelIndex& = QModelIndex()) const { return db->count(); }
    Q_INVOKABLE QObject* get(int index) const {return db->at(index);}
    QVariant data(const QModelIndex &index, int role) const;

    enum Language
    {
        English = 0,
        German = 1
    };

    enum IrregularVerbRoles
    {
        Form0Role = Qt::UserRole + 1,
        Form1Role,
        Form2Role
    };

    IrregularListWrapper();

    QString getLangName() const { return langName; }
    Q_INVOKABLE void changeLang(Language l) { beginResetModel(); db = 0; setLang(l); endResetModel(); }

    static QMap<Language, QString> plugins;

signals:
    void langChanged();
protected:
    void setLang(Language);
    QString langName;
    AbstractIrregularList * db;

};


QVariant IrregularListWrapper::data(const QModelIndex &index, int role) const
{
    if (!index.isValid()) return QVariant();

    int rowno = index.row();

    qDebug() << "Row is " << index.row() << flush;

    const IrregularVerb* verb = db->at(index.row());

    switch (role)
    {
    case Form0Role:
        return verb->getForm0();
        break;
    case Form1Role:
        return verb->getForm1();
        break;
    case Form2Role:
        return verb->getForm2();
        break;
    }
    return QVariant();
}

AbstractIrregularList:

class AbstractIrregularList : public QObject, public QList<IrregularVerb*>
{
    Q_OBJECT
public:
    void IV(const char* a, const char* b, const char* c) { append (new IrregularVerb(a, b, c)); }
    void IV(const char *a, const char *b) { IV(a, b, b); }
    void IV(const char *a) { IV(a,a,a); }
};

Q_DECLARE_INTERFACE(AbstractIrregularList, "com.marmistrz.Plugin.AbstractIrregularList/1.0");

Do you have an idea why it's happening? Thanks!

/edit1: Thanks for your reply. Would this be ok?

Q_INVOKABLE QObject* get(int index)
{
    QObject* item = db->at(index);
    QDeclarativeEngine::setObjectOwnership(item, QDeclarativeEngine::CppOwnership);
    item->setParent(this); // do I need to do this? An QList<QObject*>-child would clean it, right? 
    return item;
}

Thanks

marmistrz
  • 5,974
  • 10
  • 42
  • 94

1 Answers1

6

It looks like you have a "classic" problem using QAbstractListModel on QML side. Your IrregularListWrapper has get method which returns objects with JS ownership (that kind of ownership is default for Q_INVOKABLE methods) and they will be garbage collected with JS engine on QML side. To prevent this you should change objects' ownership before returning them to QML with QDeclarativeEngine::setObjectOwnership. See example in my answer here.

Smar
  • 8,109
  • 3
  • 36
  • 48
Pavel Osipov
  • 2,067
  • 1
  • 19
  • 27
  • Well, thanks! I updated the OP, could you please look whether the modified get(...) would be OK? Btw. why does garbage collecting at QML side hurts it so much? Thanks – marmistrz Apr 17 '13 at 15:47
  • 1
    So, updated code should work without crashes if you doesn't return objects from Q_INVOKABLE methods somewhere else. But it will be much cleaner to setup ownership once during insert, but not everytime when you provide objects to QML. – Pavel Osipov Apr 17 '13 at 19:56
  • 1
    The reason for the crashes is that when JS will destroy objects your ListWrapper will not be notified about that and it will contain dangling pointers. Then when some other object will use them (for example QML will ask your Wrapper for the objects at the same indices which it has been used and destroyed before) and access to any of its field there will be a crash – Pavel Osipov Apr 17 '13 at 20:00
  • So, it's as if I did `int * a = new int; delete a; cout << *a; `, right? – marmistrz Apr 18 '13 at 12:07
  • 1
    @marmistrz yes, that is your case. – Pavel Osipov Apr 18 '13 at 17:31
  • Out of curiosity, 4 years on, is this still a bug? – Kenn Sebesta Mar 15 '17 at 12:35
  • @KennSebesta I doubt this really is a bug, but a ”feature”. This makes sense, it just is poorly documented, or I’m not able to find it. – Smar Mar 23 '19 at 07:07