当shared_ptr超出范围时,c++崩溃..在成功运行至少100000次之后

c++ Crash when shared_ptr goes out of scope...after running at least 100,000 times successfully

本文关键字:成功 崩溃 运行 之后 100000次 c++ ptr shared 范围      更新时间:2023-10-16

此代码在崩溃之前已经运行了100000次(在测试中运行了6小时)。当崩溃发生时,当shared_ptr超出范围时就会发生。。。此函数的目的是填充sharedptr的传入向量(在本例中),使用按类型筛选的消息。。。因此,并不是historyQueue中的所有消息都会被添加。这个新填充的矢量稍后用于发送这些PM。。。这个函数也被其他线程调用,这就是为什么有一个LockGuard,它是标准LockGuard的typedef(typedefstd::lock_guard LockGuard)

bool MessageHistory::getMessages(vector< shared_ptr<ProtocolMessage> >& v,bool allMessages, bool playerFilter, int playerId, MessageFilter* filter)
{
    LockGuard lock(historyMutex);
    v.resize(historyQueue.size());
    unsigned count=0;
    for_vector(historyQueue,i)
    {   PM pm=historyQueue[i];//PM is a shared_ptr as well items in histortQueue
        const int uid=pm->GetPlayerDest();
        bool pmok =false;
        int pmtype=0;

        if(!pm->GetPid() || !pm->GetMid())
            continue;
        pmtype=(pm->GetPid() << 16) + pm->GetMid();

        if(filter && pmtype)
            pmok=filter->messageIsOk(pmtype);
        if ((allMessages || uid == -1 || (playerFilter && uid == playerId))
        && (filter == 0 || pmok))
        {   if(count>=v.size())
            {   break;
            }
            v[count++]=pm;
        }
    }//crash happens here after 100,000's of successful calling of this function
    v.resize(count);
    return true;
}

回溯:

Program terminated with signal 11, Segmentation fault.
#0  0x00007f74546b51b0 in ?? ()
(gdb) bt
#0  0x00007f74546b51b0 in ?? ()
#1  0x00000000005a7f41 in _M_release (this=0x7f7454204230)
    at /usr/include/c++/4.6/bits/shared_ptr_base.h:146
#2  ~__shared_count (this=0x7f747dff0d38, __in_chrg=<optimized out>)
    at /usr/include/c++/4.6/bits/shared_ptr_base.h:551
#3  ~__shared_ptr (this=0x7f747dff0d30, __in_chrg=<optimized out>)
    at /usr/include/c++/4.6/bits/shared_ptr_base.h:751
#4  ~shared_ptr (this=0x7f747dff0d30, __in_chrg=<optimized out>)
    at /usr/include/c++/4.6/bits/shared_ptr.h:93
#5  MessageHistory::getMessages (this=0x7e3cf18, v=..., allMessages=false, 
    playerFilter=true, playerId=-2141, filter=0x7e3cf88)
    at MessageHistory.cpp:177

调用getmessages:的相关部分函数

if (handIsActive.IsLocked() && history.size() > 0)
    {   vector< shared_ptr<ProtocolMessage> > lp;
        history.getMessages(lp,playerId,&noChatFilter);
        shared_ptr<ProtocolMessage> pm(new HandSoFar(lp));
        GameQueue::sendMessage(address, pm);
}   }

向量lp在此之后不再使用。。。

任何帮助都将不胜感激。。。。感谢

我唯一的预感是你在其他地方有UB。

它可能是你作为参考(v)传递的向量上的一场比赛。很容易忘记同步访问。


稍微偏离主题:我可以建议简化一下吗。

减少代码/分离问题总是有助于降低复杂性,从而降低错误率:

bool getMessages(vector<PM> &v, Query const& query)
{
    LockGuard lock(historyMutex);
    v.clear();
    std::copy_if(historyQueue.begin(), historyQueue.end(), back_inserter(v), query);
    return true;
}

那里有很多令人困惑/冲突的事情:

  • 条件

        if (count >= v.size()) {
            break;
        }
    

    由于resize()前置

  • v.resize(count)之后也。。。这只需要copy_if(实际上,v.assign_if会更好,但库没有想到这一点。Boost Range可以做到)。

  • 这里有太多的混合和冗余:

    if (!pm->GetPid() || !pm->GetMid()) // 1.
        continue;
    

    在这里,我们已经知道pmtype不可能为零:

    pmtype = (pm->GetPid() << 16) + pm->GetMid();
    if (filter && pmtype) // pmtype cannot be zero 
        pmok = filter->messageIsOk(pmtype);
    

    pmok现在是部分定义的,只有当filter!=0。。。然后它在使用中变得毛茸茸:

    if ((allMessages || uid == -1 || (playerFilter && uid == playerId)) && (filter == 0 || pmok)) {
    

    显然做会更容易

    bool pmok = !filter || filter->messageIsOk(pmtype);
    if ((allMessages || uid == -1 || (playerFilter && uid == playerId)) && pmok) {
    
  • 我认为allMessages没有做到它所说的是不确定的。它实际上做了一些与playerFilter==false现在所实现的相反的事情。我认为很可能是你把括号放错地方了,你应该调用标志allUserIds?(我假设是后者)。

  • 您可以使用optional<int> playerId简化界面。然而,既然uid==-1显然是一个神奇的值,为什么不让-1也发出"没有用户ID过滤器"的信号呢?

  • 什么是for_vector(当然……它不是用来写循环的宏?再说一遍,这就是copy_if的作用)。

使用这些调整,Query类可以像这样完全实现:

struct Query {
    bool allPlayers;
    int playerId;
    MessageFilter *filter;
    Query(bool allPlayers = true, int playerId = -1, MessageFilter* filter = nullptr) :
        allPlayers(allPlayers), playerId(playerId), filter(filter)
    {
    }
    bool operator()(PM const& pm) const {
        if (!(pm->GetPid() && pm->GetMid()))
            return false;
        int  pmtype = (pm->GetPid() << 16) + pm->GetMid();
        bool pmok   = !filter || filter->messageIsOk(pmtype);
        int  uid         = pm->GetPlayerDest();
        bool matchPlayer = uid == -1 || allPlayers || (playerId != -1 && uid == playerId);
        return pmok && matchPlayer;
    }
};

其用途如

std::vector<MessageHistory::PM> v;
hist.getMessages(v, MessageHistory::Query { false, 42 }); // only uid 42; no MessageFilter

完整演示

在Coliru上直播

#include <vector>
#include <mutex>
#include <memory>
#include <deque>
#include <algorithm>
using LockGuard = std::lock_guard<std::mutex>;
std::mutex historyMutex;
struct ProtocolMessage { //stub
    int GetPlayerDest() const { return 1; }
    int GetPid()        const { return 1; }
    int GetMid()        const { return 1; }
};
struct MessageFilter {
    virtual bool messageIsOk(int)  const { return true; }
    virtual ~MessageFilter() { }
};
struct MessageHistory 
{
    using PM = std::shared_ptr<ProtocolMessage>;
    struct Query {
        bool allPlayers;
        int playerId;
        MessageFilter *filter;
        Query(bool allPlayers = true, int playerId = -1, MessageFilter* filter = nullptr) :
            allPlayers(allPlayers), playerId(playerId), filter(filter)
        {
        }
        bool operator()(PM const& pm) const {
            if (!(pm->GetPid() && pm->GetMid()))
                return false;
            int  pmtype = (pm->GetPid() << 16) + pm->GetMid();
            bool pmok   = !filter || filter->messageIsOk(pmtype);
            int  uid         = pm->GetPlayerDest();
            bool matchPlayer = uid == -1 || allPlayers || (playerId != -1 && uid == playerId);
            return pmok && matchPlayer;
        }
    };
    bool getMessages(std::vector<PM> &v, Query const& query)
    {
        LockGuard lock(historyMutex);
        v.clear();
        std::copy_if(historyQueue.begin(), historyQueue.end(), back_inserter(v), query);
        return true;
    }
  private:
    std::deque<PM> historyQueue;
};
int main() {
    MessageHistory hist;
    std::vector<MessageHistory::PM> v;
    hist.getMessages(v, MessageHistory::Query { false, 42 }); // only uid 42; no MessageFilter
}