2
votes

I'm currently stuck with regards to segfaults (sometimes sigabrts due to bad mallocs) whenever I try add a QString to a QMap as key inside the destructor of QWidget class I have - I think it has something to do with QString's implicit sharing model and scope.

I have a QWidget acting as a sub window in an MDI, this QWidget has a few QGLWidget derived viewport instances as children. In the sub window there is a QMap< QString, QVariant > wrapper class that contains the project file's settings, when the sub window is closed, it's destructor calls QWidget::deleteChildren() which deletes each of the viewports. In the viewport destructor the current settings are saved into the sub window's settings, eg:

QString dir = QString( "camera/" ) + name;
sWin.setSetting( dir + "/Projection",
                 static_cast< int >( camera_->getProjectionType() ) );

The sWin.setSetting() is called for each of the properties I wish to save, 'sWin' is a reference to the QMap wrapper class that is deleted at the end of the sub window destructor. Everything is fine until the setSetting() call, which is just:

inline void setSetting( const QString& key, QVariant value )
{
    //  projectSettings_ is a standard QMap< QString, QVariant >.
    projectSettings_.insert( key, value );
}

This setup works fine for the first viewport, at the first setSetting() call of the second, a segfault occurs at:

inline QString::QString(const QString &other) : d(other.d)
{ Q_ASSERT(&other != this); d->ref.ref(); }

Am I seeing a deep copy fail when my QString reference is passed to the QMap? If so, why? The QString I created has not gone out of scope yet as the destructor has not returned. And why would this fail on the second viewport and not the first?

Occasionally I will get a sigabrt malloc(): memory corruption in operator+ in the setSetting() line of the first code example . But again, at the start of the second viewport's destruction, not the first.

I apologise for the very wordy question, but there is a large amount of code involved spread across many translation units. Any clues for this problem would be a great help!

Thanks in advance. Cam


Update

I changed my first code sample to:

QString* dir = new QString( "camera/" );
*dir += name;
sWin.setSetting( *dir + "/Projection",
                 static_cast< int >( camera_->getProjectionType() ) );

As a test for scope issues. And it sometimes works, and other times gives exactly the same error; so presumably the heap QString is being destroyed, it just depends on whether or not it's memory location has been overwritten. But this also makes no sense, as I am not calling delete on it, nor am I closing the application (just an MDI sub window)!


More Code

sWin is created on the heap when a new sub window is instantiated. It is brought into the viewport class' destructor as a reference from a method from my sub window:

inline Sy_project& getProject() { return *project_; }

Sy_project is (currently) a simple wrapper class for QMap. My complete viewport base class destructor is this (it fails here, not in the derived class):

Sy_abstractGLViewport::~Sy_abstractGLViewport()
{
    //  Write last viewport settings.
    QString name = objectName();
    Sy_project& sWin = projWindow_->getProject();
    QString dir = QString( "camera/" ) + name;

    //  Avoid "taking address of temporary" warning.
    QVector3D pos = camera_->getPosition();
    QVector3D foc = camera_->getFocalPoint();

    //  Save camera settings.  Dies on first setSetting() call.
    sWin.setSetting( dir + "/Projection",
                     static_cast< int >( camera_->getProjectionType() ) );
    sWin.setSetting( dir + "/position",
                     QVariant( 84, static_cast< void* >( &pos ) ) );
    sWin.setSetting( dir + "/focalPoint",
                     QVariant( 84, static_cast< void* >( &foc ) ) );
    sWin.setSetting( dir + "/FOV", camera_->getFOV() );
    sWin.setSetting( dir + "/nearClip", camera_->getPerspectiveClipRange()[0] );
    sWin.setSetting( dir + "/farClip", camera_->getPerspectiveClipRange()[1] );

    delete camera_;
}

Valgrind

After using Valgrind's memcheck I discovered numerous entries that looked similar to my stacktrace. Having never used it before, I'm still deciphering, but is this saying that my Sy_project class (wrapper for QMap) has been deleted after the setSetting() call leaving QMap with an invalid reference?

==12418== Invalid read of size 4
==12418==    at 0x805D872: QMap<QString, QVariant>::detach_helper() (qmap.h:730)
==12418==    by 0x805D380: QMap<QString, QVariant>::detach() (in /home/cbamber85/workspace/Syren GUI/Syren)
==12418==    by 0x805CDEE: QMap<QString, QVariant>::insert(QString const&, QVariant const&) (qmap.h:537)
==12418==    by 0x805CA33: Sy_project::setSetting(QString const&, QVariant) (Sy_project.h:50)
==12418==    by 0x805A78C: Sy_abstractGLViewport::~Sy_abstractGLViewport() (Sy_abstractGLViewport.cpp:67)
==12418==    by 0x808EDBC: Sy_QtGLViewport::~Sy_QtGLViewport() (Sy_QtGLViewport.cpp:91)
==12418==    by 0x808EE0E: Sy_QtGLViewport::~Sy_QtGLViewport() (Sy_QtGLViewport.cpp:100)
==12418==    by 0x4D66D63: QObjectPrivate::deleteChildren() (in /usr/lib/libQtCore.so.4.6.3)
==12418==    by 0x4306DDF: QWidget::~QWidget() (in /usr/lib/libQtGui.so.4.6.3)
==12418==    by 0x46FBE0E: QFrame::~QFrame() (in /usr/lib/libQtGui.so.4.6.3)
==12418==    by 0x475F173: QSplitter::~QSplitter() (in /usr/lib/libQtGui.so.4.6.3)
==12418==    by 0x475F1D1: QSplitter::~QSplitter() (in /usr/lib/libQtGui.so.4.6.3)
==12418==  Address 0xacf5b9c is 4 bytes inside a block of size 8 free'd
==12418==    at 0x40266AD: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==12418==    by 0x808D60F: Sy_project::~Sy_project() (Sy_project.h:30)
==12418==    by 0x808C9C6: Sy_subWindow::~Sy_subWindow() (Sy_subWindow.cpp:55)
==12418==    by 0x808CA84: Sy_subWindow::~Sy_subWindow() (Sy_subWindow.cpp:57)
==12418==    by 0x4D66482: qDeleteInEventHandler(QObject*) (in /usr/lib/libQtCore.so.4.6.3)
==12418==    by 0x4D67967: QObject::event(QEvent*) (in /usr/lib/libQtCore.so.4.6.3)
==12418==    by 0x4302ACB: QWidget::event(QEvent*) (in /usr/lib/libQtGui.so.4.6.3)
==12418==    by 0x42A9C63: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /usr/lib/libQtGui.so.4.6.3)
==12418==    by 0x42B1CA3: QApplication::notify(QObject*, QEvent*) (in /usr/lib/libQtGui.so.4.6.3)
==12418==    by 0x806010F: Sy_application::notify(QObject*, QEvent*) (Sy_application.cpp:14)
==12418==    by 0x4D54E0D: QCoreApplication::notifyInternal(QObject*, QEvent*) (in /usr/lib/libQtCore.so.4.6.3)
==12418==    by 0x4D589B3: QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (in /usr/lib/libQtCore.so.4.6.3)
2
Where and how is sWin created and when and how exactly is it deleted (or gets deleted by you)? - bjoern.bauer
Swin is created when a new sub window is instantiated - before any gui components are created. It is then deleted at the end of the sub window destructor, my sub window class is derived from QWidget, so QWidget's destructor calls delete on it's children (my viewport objects) before my sub window destructor code is run. So sWin should be in scope at all times, this side of things appears to be working correctly as the stack trace looks right. - cmannett85
Is the sWin member in your sub window class declared as a pointer or as an object? I think it would help if you could post more of your code, for instance at least the complete destructor and the part of the constructor where you create sWin. I'm a little bit confused because you say sWin is a reference. As the descrutor can't have parameters, the reference would have to be declared within the descrutor, which seems odd, and could hint to the cause of your problem. - bjoern.bauer

2 Answers

0
votes

Hum... What happens if you change your code for something like this:

dir.append("/Projection");
sWin.setSetting(dir, static_cast<int>(camera_->getProjectionType()));

In the way you are doing a temporary copy is create and then bound to a reference-to-const, which should be ok. Afterwards, from this reference QMap will attempt to copy the argument, in this case, going through the implicit sharing mechanism of QString. I'm just wondering what could go wrong in there...

0
votes

I think the Sy_project object your pointer and therefore references are pointing to is already destroyed by the time the destructor of Sy_abstractGLViewport is called. If you look into the valgrind listing, the destructor of Sy_project is called before the destructor of Sy_abstractGLViewport.

So when you call inline Sy_project& getProject() { return *project_; } within the Sy_abstractGLViewport destructor, you're dereferencing a dangling pointer.