2013-03-15 7 views
8

Gestern geriet ich in Misere, was mich 24 Stunden frustriert. Das Problem ist auf unerwartete Abstürze bei zufällig Basis zurückgekommen. Um die Dinge zu komplizieren, hatte Debugging-Berichte absolut zufällige Muster. Um es noch komplizierter zu machen, führten alle Debugging-Traces entweder zu zufälligen Qt-Quellen oder nativen DLLs, d. H. Beweisen jedes Mal, wenn das Problem eher nicht auf meiner Seite ist.Pimpl + QSharedPointer - Destructor = Katastrophe

Hier sind Sie ein paar Beispiele für solche schöne Berichte:

Program received signal SIGSEGV, Segmentation fault. 
0x0000000077864324 in ntdll!RtlAppendStringToString() from C:\Windows\system32\ntdll.dll 
(gdb) bt 
#0 0x0000000077864324 in ntdll!RtlAppendStringToString() from C:\Windows\system32\ntdll.dll 
#1 0x000000002efc0230 in ??() 
#2 0x0000000002070005 in ??() 
#3 0x000000002efc0000 in ??() 
#4 0x000000007787969f in ntdll!RtlIsValidHandle() from C:\Windows\system32\ntdll.dll 
#5 0x0000000000000000 in ??() 

warning: HEAP: Free Heap block 307e5950 modified at 307e59c0 after it was freed 
Program received signal SIGTRAP, Trace/breakpoint trap. 
0x00000000778bf0b2 in ntdll!ExpInterlockedPopEntrySListFault16() from C:\Windows\system32\ntdll.dll 
(gdb) bt 
#0 0x00000000778bf0b2 in ntdll!ExpInterlockedPopEntrySListFault16() from C:\Windows\system32\ntdll.dll 
#1 0x000000007786fd34 in ntdll!RtlIsValidHandle() from C:\Windows\system32\ntdll.dll 
#2 0x0000000077910d20 in ntdll!RtlGetLastNtStatus() from C:\Windows\system32\ntdll.dll 
#3 0x00000000307e5950 in ??() 
#4 0x00000000307e59c0 in ??() 
#5 0x00000000ffffffff in ??() 
#6 0x0000000000220f10 in ??() 
#7 0x0000000077712d60 in WaitForMultipleObjectsEx() from C:\Windows\system32\kernel32.dll 
#8 0x0000000000000000 in ??() 

Program received signal SIGSEGV, Segmentation fault. 
0x0000000000a9678a in QBasicAtomicInt::ref (this=0x8) at ../../include/QtCore/../../../qt-src/src/corelib/arch/qatomic_x86_64.h:121 
121 : "memory"); 
(gdb) bt 
#0 0x0000000000a9678a in QBasicAtomicInt::ref (this=0x8) at ../../include/QtCore/../../../qt-src/src/corelib/arch/qatomic_x86_64.h:121 
#1 0x00000000009df08e in QVariant::QVariant (this=0x21e4d0, p=...) at d:/Distributions/qt-src/src/corelib/kernel/qvariant.cpp:1426 
#2 0x0000000000b4dde9 in QList<QVariant>::value (this=0x323bd480, i=1) at ../../include/QtCore/../../../qt-src/src/corelib/tools/qlist.h:666 
#3 0x00000000009ccff7 in QObject::property (this=0x3067e900, 
name=0xa9d042a <QCDEStyle::drawPrimitive(QStyle::PrimitiveElement, QStyleOption const*, QPainter*, QWidget const*) const::pts5+650> "_q_stylerect") 
at d:/Distributions/qt-src/src/corelib/kernel/qobject.cpp:3742 
#4 0x0000000000000000 in ??() 

Wie Sie dieses Zeug ziemlich böse zu sehen ist, gibt es eine keine nützlichen Informationen . Aber da war eine Sache, auf die ich nicht geachtet habe. Es war eine seltsame Warnung während der Kompilierung, die auch schwer ist, mit einem Auge zu fangen:

In file included from d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer.h:50:0, 
       from d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/QSharedPointer:1, 
       from ../../../../source/libraries/Project/sources/Method.hpp:4, 
       from ../../../../source/libraries/Project/sources/Slot.hpp:4, 
       from ../../../../source/libraries/Project/sources/Slot.cpp:1: 
d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer_impl.h: In instantiation of 'static void QtSharedPointer::ExternalRefCount<T>::deref(QtSharedPointer::ExternalRefCount<T>::Data*, T*) [with T = Project::Method::Private; QtSharedPointer::ExternalRefCount<T>::Data = QtSharedPointer::ExternalRefCountData]': 
d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer_impl.h:336:11: required from 'void QtSharedPointer::ExternalRefCount<T>::deref() [with T = Project::Method::Private]' 
d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer_impl.h:401:38: required from 'QtSharedPointer::ExternalRefCount<T>::~ExternalRefCount() [with T = Project::Method::Private]' 
d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer_impl.h:466:7: required from here 
d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer_impl.h:342:21: warning: possible problem detected in invocation of delete operator: [enabled by default] 
d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer_impl.h:337:28: warning: 'value' has incomplete type [enabled by default] 

Eigentlich habe ich auf diese Warnung wandte nur als letzter Ausweg, weil in einem solchen verzweifelten Streben, einen Fehler zu finden, war der Code bereits wörtlich mit dem Abholzen zu Tode infiziert.

Nachdem es sorgfältig zu lesen, erinnerte ich mich, dass, zum Beispiel, wenn man std::unique_ptr oder std::scoped_ptr für Pimpl verwendet - ein sicherlich desctructor bieten sollte, sonst wird der Code nicht einmal kompilieren. Ich erinnere mich aber auch, dass std::shared_ptr Destruktor nicht kümmert und ohne es gut funktioniert. Es war ein weiterer Grund, warum ich diese seltsame Warnung nicht beachtet habe. Lange Rede, kurzer Sinn, als ich Destruktor hinzugefügt habe, hat dieser zufällige Absturz aufgehört. Sieht aus, als hätte Qt's QSharedPointer einige Designfehler im Vergleich zu std::shared_ptr. Ich denke, es wäre besser, wenn Qt-Entwickler diese Warnung umgewandelt in Fehler weil das Debuggen Marathons wie die sind einfach nicht wert seine Zeit, Mühe und Nerven.

Meine Fragen sind:

  1. Was mit QSharedPointer falsch? Warum Destruktor ist so wichtig?
  2. Warum Absturz passiert, wenn es keinen Destruktor gab? Diese Objekte (die Pimpl + QSharedPointer verwenden) werden auf dem Stapel erstellt, und nach ihrem Tod haben keine anderen Objekte mehr Zugriff auf sie. Allerdings kam es während einiger Zeit nach ihrem Tod zu einem Absturz während einiger zufällige.
  3. Hat jemand solche Probleme schon einmal erlebt? Bitte teilen Sie Ihre Erfahrung.
  4. Gibt es andere Fallstricke wie in Qt - diejenigen, die ich muss wissen über sicher zu bleiben sicher in der Zukunft?

Hoffentlich werden diese Fragen und mein Beitrag im Allgemeinen anderen helfen, die Hölle zu vermeiden, in der ich in den letzten 24 Stunden gewesen bin.

+2

Ich würde generell die Verwendung des Flags -Werror empfehlen, um alle Warnungen in Fehler umzuwandeln. Es erfordert einige Arbeit, alle Warnungen zu sortieren, aber ich habe es als unschätzbar für die Warnung vor möglichen Problemen (ob Sie Qt verwenden oder nicht) gefunden. – cgmb

+1

Der Unterschied zwischen QSharedPtr und shard_ptr besteht darin, wie und wann der Löschcode von T * instanziiert wird - und dieser Code muss natürlich die Deklaration von T kennen, um den dtor aufzurufen. Die Heilung besteht darin, Warnungen ernst zu nehmen (wie von Slavik81 vorgeschlagen). –

Antwort

3

Das Problem um in Qt 5, siehe https://codereview.qt-project.org/#change,26974

Der Compiler die falschen destructor Aufruf oder ein anderer Speicher unter der Annahme, Layout wahrscheinlich zu irgendeiner Art von Speicherbeschädigung führt gearbeitet wurde. Ich würde sagen, dass ein Compiler für dieses Problem einen Fehler und keine Warnung geben sollte.

1

Sie stoßen auf ein ähnliches Problem mit std::unique_ptr, die auch gebrochene Destruktoren verursachen kann, wenn sie mit einem unvollständigen Typ verwendet werden. Die Lösung ist ziemlich trivial, natürlich - Ich erkläre einen Konstruktor für die Klasse, sie dann als

MyClass::~MyClass() = default; 

Den Grund, in der Implementierungsdatei festlegen, dass dies ein Problem für std::unique_ptr ist aber nicht std::shared_ptr ist, dass die destructor Teil ist von der Art der ersteren, aber ist ein Mitglied der letzteren.