QSharedData崩溃 [英] QSharedData crash

查看:858
本文介绍了QSharedData崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个罕见但相当一致的崩溃在我的Qt 5.2.0应用程序,我有一个时间诊断,但相信与相关 QSharedData 。应用程序是高度多线程的,这可能是问题的一部分。



有问题的类是这里:

  class RouteData:public QSharedData 
{
public:
RouteData():
m_destAddress(0),
(false),
m_movingAverage(ROUTE_INITIAL_QUALITY)
{}
RouteData(const RouteData& other):
QSharedData(other),
m_destAddress(other.m_destAddress) ,
m_addresses(other.m_addresses),
m_valid(other.m_valid),
m_movingAverage(other.m_movingAverage),
m_lastSuccess(other.m_lastSuccess),
m_lastFailure (other.m_lastFailure)
{}
〜RouteData(){}

quint16 m_destAddress;
QList< quint16> m_addresses;
bool m_valid;
double m_movingAverage;
QDateTime m_lastSuccess;
QDateTime m_lastFailure;
};

class Route
{
public:
Route()
{
d = new RouteData;
}
路由(quint16 destAddress)
{
d = new RouteData;
d-> m_destAddress = destAddress;
}
Route(const Route& other):d(other.d){}

QString toString()const;

bool isValid()const {return d-> m_valid; }
quint16 destAddress()const {return d-> m_destAddress; }
QList< quint16> addressList()const {return d-> m_addresses; }
quint32 length()const {return d-> m_addresses.length(); }
double quality()const {return d-> m_movingAverage; }
quint64 msecsSinceLastFailure()const;

void setDestination(quint16 dest){d-> m_destAddress = dest; }
void setAddressList(const QList< quint16>& addressList);
void markResult(bool success);

bool operator<(const Route& other)const;
bool operator ==(const Route& other)const;
bool operator!=(const Route& other)const;

private:
QSharedDataPointer< RouteData> d;
};

Q_DECLARE_TYPEINFO(Route,Q_MOVABLE_TYPE);

崩溃发生在这里,将一个Route插入到 QMap

  SendResult EmberGateway :: ezspSendUnicast(quint16 indexOrDestination,...,const Route& route)
{
...
if(route.isValid()){
m_lastRouteMap.insert(indexOrDestination,route);

其中m_lastRouteMap是一个 QMap< quint16,Route>



堆栈跟踪看起来像这样:

  (gdb)其中
#0 0x00007fa297ced9a8在QTimeZone ::〜QTimeZone()()从/usr/local/Trolltech/Qt-5.2.0/lib/libQt5Core.so.5
#1 0x00007fa297c96de5在RouteData ::〜RouteData中,来自/usr/local/Trolltech/Qt-5.2.0/lib/libQt5Core.so.5
#2 0x00000000004644fb的QDateTime ::〜QDateTime()(this = 0x7fa28c00b150,__in_chrg = < optimization out>)at ../libILS/libILS/Route.h:33
#3 0x0000000000600805 in QSharedDataPointer< RouteData> :: operator =(this = 0x7fa28c0e6420,o = ...)
在/usr/local/Trolltech/Qt-5.2.0/include/QtCore/qshareddata.h:98
#4 0x00000000005ff55b在Route :: operator =(this = 0x7fa28c0e6420)at ./libILS/Route.h: 43
#5 0x0000000000652f8e in QMap< unsigned short,Route> :: insert(this = 0x7fa28c0880e8,akey = @ 0x7fa17c6feb44:25504,avalue = ...)
at / usr / local / Trolltech / Qt -52.0 / include / QtCore / qmap.h:682
#6 0x0000000000641b4b in ils :: EmberGateway :: ezspSendUnicast(this = 0x7fa28c088090,indexOrDestination = 25504,apsFrame = ...,
data = ...,route = ...)at libls / Gateways / EmberGateway.cpp:909
#7 0x00000000006371d5 in ils :: EmberGateway :: sendUnicast(this = 0x7fa28c088090,destAddress = 25504,array = network = ...)
at libILS / Gateways / EmberGateway.cpp:474
#8 0x00000000005fadc4在NetworkController :: sendMessageViaGateway(this = 0x7fa28c03e9b0,message = ...)
at libILS / NetworkController :: dispatchMessage中的控制器/ NetworkController.cpp:1668
#9 0x00000000005ed8f4(这是= 0x7fa28c03e9b0,pendingMessagePair = ...)libILS / Controllers / NetworkController.cpp下的

#10 0x0000000000604e2f在QtConcurrent :: VoidStoredMemberFunctionPointerCall1< void,NetworkController,QPair< QSharedPointer< Message>,QTimer *>,QPair< QSharedPointer< Message>,QTimer * > :: runFunctor(this = 0x7fa23804ac60)
at /usr/local/Trolltech/Qt-5.2.0/include/QtConcurrent/qtconcurrentstoredfunctioncall.h:410
#11 0x00000000005ff41e in QtConcurrent :: RunFunctionTask< void> :: run(this = 0x7fa23804ac60)
at /usr/local/Trolltech/Qt-5.2.0/include/QtConcurrent/qtconcurrentrunbase.h:132
#12 0x00007fa297c55e52 in? ()from /usr/local/Trolltech/Qt-5.2.0/lib/libQt5Core.so.5
#13 0x00007fa297c591c2 in? ()从/usr/local/Trolltech/Qt-5.2.0/lib/libQt5Core.so.5
#14 0x00007fa297746f3a在start_thread()从/lib64/libpthread.so.0
#15 0x00007fa296c698fd in clone()from /lib64/libc.so.6

QMap :: insert,并在#4创建一个副本(通过Route=运算符)。在#3处,Qt代码问题如下:

  inline QSharedDataPointer< T& & operator =(const QSharedDataPointer< T& o){
if(o.d!= d){
if(o.d)
o.d-&
T * old = d;
d = o.d;
if(old&&!old-> ref.deref())
delete old;
}
return * this;
}

我们正在击中删除旧 QDateTime 的其中一个(实际上是私人的 QTimeZone 成员)。



我的 ezspSendUnicast()方法可以运行良好的数十万次迭代之前崩溃。我不认为我泄漏记忆(根据valgrind)。我相信我传递给ezspSendUnicast()的Route对象正确地互斥保护,但是我可能错过了一些东西(但我认为 QSharedData 是线程安全的对于任何情况下的写时复制)。



非常感谢任何有关如何解决这个问题的洞察力。

单独的实例访问<$ c>时, QSharedData 的实例 $ c> QSharedDataPointer
,实际上,可以从多个线程一次访问,它是安全的。



因此,只要你在自己的实例

上工作,那么共享数据指针就会自动获取引用数据的副本。 em> Route 对象,一切都很好。你不能做的是使用引用到容器所持有的元素。您可以使用const引用来构建从容器所持有的临时对象(即新实例)。



根据文档


在线程之间隐式共享类时,共享一个实例


从不正确访问对共享实例的引用



您必须总是有一个新的实例。您可以复制构造一个临时实例,然后通过const引用传递它,例如作为函数调用中的参数。 这样的const引用-temporary-instances延迟被引用临时对象的销毁,直到它们不再需要



这适用于Qt中所有隐式共享的类4和Qt 5 - 无论是来自Qt(所有容器!)还是您自己的设计,利用 QSharedDataPointer



所以,这是正确的 - 你保留自己的实例。

 路由r(...); 
QMutexLocker l(& routeMapMutex);
routeMap.insert(key,r);
l.unlock();
r.setDestination(...);
QMutexLocker l(& routeMapMutex);
routeMap.insert(key,r);
l.unlock();

正如这样 - 您还有自己的实例。

  QMutexLocker l(& routeMapMutex); 
路由r = routeMap [key];
l.unlock();
if(r.isValid())...

即使引用是const。

  QMutexLocker l(& routeMapMutex); 
const Route& r = routeMap [key];
l.unlock();
if(r.isValid())...

但这是正确的,因为它是对临时实例的常量引用,其寿命根据需要延长。所以,你引用一个新的实例,没有人可以访问。

  QMutexLocker l(& routeMapMutex); 
const Route& r = Route(routeMap [key]);
l.unlock();
if(r.isValid())...

该调用由互斥量保护:

  void fun(const Route& 
QMutexLocker l(& routeMapMutex);
fun(routeMap [key]);
l.unlock();

我的预感是在代码中的某个地方访问对地图中的值的引用到一个const-ref到一个临时的)没有持有互斥。



也可以想象,你有一些其他内存或线程的错误,表现在一个无关的地方。


I have an infrequent but fairly consistent crash in my Qt 5.2.0 application which I am having a heck of a time diagnosing, but believe to be related to QSharedData. The application is highly multi-threaded, which is presumably part of the problem.

The class in question is here:

class RouteData : public QSharedData
{
public:
  RouteData() :
      m_destAddress(0),
      m_valid(false),
      m_movingAverage(ROUTE_INITIAL_QUALITY)
  { }
  RouteData(const RouteData &other) :
      QSharedData(other),
      m_destAddress(other.m_destAddress),
      m_addresses(other.m_addresses),
      m_valid(other.m_valid),
      m_movingAverage(other.m_movingAverage),
      m_lastSuccess(other.m_lastSuccess),
      m_lastFailure(other.m_lastFailure)
  { }
  ~RouteData() { }

  quint16           m_destAddress;
  QList<quint16>    m_addresses;
  bool              m_valid;
  double            m_movingAverage;
  QDateTime         m_lastSuccess;
  QDateTime         m_lastFailure;
};

class Route
{
public:
    Route()
    {
        d = new RouteData;
    }
    Route(quint16 destAddress)
    {
        d = new RouteData;
        d->m_destAddress = destAddress;
    }
    Route(const Route &other) : d(other.d) {}

    QString toString() const;

    bool            isValid() const         { return d->m_valid; }
    quint16         destAddress() const     { return d->m_destAddress; }
    QList<quint16>  addressList() const     { return d->m_addresses; }
    quint32         length() const          { return d->m_addresses.length(); }
    double          quality() const         { return d->m_movingAverage; }
    quint64         msecsSinceLastFailure() const;

    void            setDestination(quint16 dest) { d->m_destAddress = dest; }
    void            setAddressList(const QList<quint16> &addressList);
    void            markResult(bool success);

    bool operator<(const Route& other) const;
    bool operator==(const Route& other) const;
    bool operator!=(const Route& other) const;

private:
    QSharedDataPointer<RouteData> d;
};

Q_DECLARE_TYPEINFO(Route, Q_MOVABLE_TYPE);

The crash occurs here, inserting a Route into a QMap:

SendResult EmberGateway::ezspSendUnicast(quint16 indexOrDestination, ..., const Route& route)
{
...
    if (route.isValid()) {
        m_lastRouteMap.insert(indexOrDestination, route);

where m_lastRouteMap is a QMap<quint16, Route>.

And the stack trace looks like this:

(gdb) where
#0  0x00007fa297ced9a8 in QTimeZone::~QTimeZone() () from /usr/local/Trolltech/Qt-5.2.0/lib/libQt5Core.so.5
#1  0x00007fa297c96de5 in QDateTime::~QDateTime() () from /usr/local/Trolltech/Qt-5.2.0/lib/libQt5Core.so.5
#2  0x00000000004644fb in RouteData::~RouteData (this=0x7fa28c00b150, __in_chrg=<optimized out>) at ../libILS/libILS/Route.h:33
#3  0x0000000000600805 in QSharedDataPointer<RouteData>::operator= (this=0x7fa28c0e6420, o=...)
    at /usr/local/Trolltech/Qt-5.2.0/include/QtCore/qshareddata.h:98
#4  0x00000000005ff55b in Route::operator= (this=0x7fa28c0e6420) at ./libILS/Route.h:43
#5  0x0000000000652f8e in QMap<unsigned short, Route>::insert (this=0x7fa28c0880e8, akey=@0x7fa17c6feb44: 25504, avalue=...)
    at /usr/local/Trolltech/Qt-5.2.0/include/QtCore/qmap.h:682
#6  0x0000000000641b4b in ils::EmberGateway::ezspSendUnicast (this=0x7fa28c088090, indexOrDestination=25504, apsFrame=..., 
    data=..., route=...) at libILS/Gateways/EmberGateway.cpp:909
#7  0x00000000006371d5 in ils::EmberGateway::sendUnicast (this=0x7fa28c088090, destAddress=25504, array=..., route=...)
    at libILS/Gateways/EmberGateway.cpp:474
#8  0x00000000005fadc4 in NetworkController::sendMessageViaGateway (this=0x7fa28c03e9b0, message=...)
    at libILS/Controllers/NetworkController.cpp:1668
#9  0x00000000005ed8f4 in NetworkController::dispatchMessage (this=0x7fa28c03e9b0, pendingMessagePair=...)
    at libILS/Controllers/NetworkController.cpp:913
#10 0x0000000000604e2f in QtConcurrent::VoidStoredMemberFunctionPointerCall1<void, NetworkController, QPair<QSharedPointer<Message>, QTimer*>, QPair<QSharedPointer<Message>, QTimer*> >::runFunctor (this=0x7fa23804ac60)
    at /usr/local/Trolltech/Qt-5.2.0/include/QtConcurrent/qtconcurrentstoredfunctioncall.h:410
#11 0x00000000005ff41e in QtConcurrent::RunFunctionTask<void>::run (this=0x7fa23804ac60)
    at /usr/local/Trolltech/Qt-5.2.0/include/QtConcurrent/qtconcurrentrunbase.h:132
#12 0x00007fa297c55e52 in ?? () from /usr/local/Trolltech/Qt-5.2.0/lib/libQt5Core.so.5
#13 0x00007fa297c591c2 in ?? () from /usr/local/Trolltech/Qt-5.2.0/lib/libQt5Core.so.5
#14 0x00007fa297746f3a in start_thread () from /lib64/libpthread.so.0
#15 0x00007fa296c698fd in clone () from /lib64/libc.so.6

So at #5 we're doing the QMap::insert, and at #4 creating a copy (via Route "=" operator). At #3 the Qt code in question looks like this:

inline QSharedDataPointer<T> & operator=(const QSharedDataPointer<T> &o) {
    if (o.d != d) {
        if (o.d)
            o.d->ref.ref();
        T *old = d;
        d = o.d;
        if (old && !old->ref.deref())
            delete old;
    }
    return *this;
}

We're hitting the "delete old" and seg faulting in the dtor for one of the QDateTime's (actually it's private QTimeZone member).

My ezspSendUnicast() method can run fine for hundreds of thousands of iterations before crashing. I don't think I'm leaking memory (according to valgrind). I believe that the Route object I'm passing to ezspSendUnicast() is properly mutex protected, but it's possible I've missed something (but I thought the QSharedData was thread-safe for copy-on-write, in any case).

Any insight on how to tackle this problem would be greatly appreciated!

解决方案

The instances of QSharedData, when accessed via a separate instance of QSharedDataPointer, can be, in fact, accessed from multiple threads at once, and it's safe. The shared data pointer will atomically take a copy of the referenced data, as needed.

So, as long as you're working on your own instance of the Route object, everything is fine. What you can't do is use a reference to an element held by a container. You can use a const-reference to a temporary object constructed from the one held by the container - that is, to a new instance.

Per the documentation:

Proper locking should be used when sharing an instance of an implicitly shared class between threads.

It is never correct to access a reference to a shared instance of an implicitly shared class without holding the lock.

You must always have a new instance. You can copy construct a temporary instance and then pass it via a const reference, for example as an argument in a function call. Such const-references-to-temporary-instances delay the destruction of the referenced temporary object until they are not needed anymore.

This applies to all implicitly shared classes in Qt 4 and Qt 5 -- whether coming from Qt proper (all containers!), or of your own design that leverages QSharedDataPointer.

So, this would be correct - you keep your own instance.

Route r(...);
QMutexLocker l(&routeMapMutex);
routeMap.insert(key, r);
l.unlock();
r.setDestination(...);
QMutexLocker l(&routeMapMutex);
routeMap.insert(key, r);
l.unlock();

As is this - again, you have your own instance.

QMutexLocker l(&routeMapMutex);
Route r = routeMap[key];
l.unlock();
if (r.isValid()) ...

But this is certainly incorrect, even though the reference is const.

QMutexLocker l(&routeMapMutex);
const Route & r = routeMap[key];
l.unlock();
if (r.isValid()) ...

But this is correct, since it is a const reference to a temporary instance, whose lifetime is prolonged as needed. So, you reference a new instance that nobody but you have access to.

QMutexLocker l(&routeMapMutex);
const Route & r = Route(routeMap[key]);
l.unlock();
if (r.isValid()) ...

This is also correct, since the call is guarded by the mutex:

void fun(const Route &);
QMutexLocker l(&routeMapMutex);
fun(routeMap[key]);
l.unlock();

My hunch is that somewhere in your code you access the reference to the value in the map (as opposed to a const-ref to a temporary) without holding the mutex.

It's also conceivable that you are having some other memory or threading bug that manifests itself in an unrelated place.

这篇关于QSharedData崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆