shared_ptr 超出范围时 c++ 崩溃...在 运行 至少成功 100,000 次之后
c++ Crash when shared_ptr goes out of scope...after running at least 100,000 times successfully
此代码在崩溃之前有 运行 100,000 次(测试中 运行 宁 6 小时)。当崩溃发生时,它会在 shared_ptr 超出范围时发生......此函数的目的是填充传入的 shared_ptr 向量(在这种情况下),并按类型过滤消息。 ..所以并不是 historyQueue 中的所有消息都被添加。这个新填充的向量稍后用于发送这些 PM...这个函数也被其他线程调用,这就是为什么有一个 LockGuard,它是标准锁卫的类型定义(typedef std::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
) 上的竞赛。很容易忘记同步访问。
稍微跑题了:我可以建议稍微简化一下吗。
减少 code/separating 问题总是有助于保持复杂性,从而降低错误率:
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;
}
那里有很多 confusing/conflicting 东西:
条件
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
信号也设为 'no userid filter' 呢?
什么是for_vector
(确定...写循环不是宏?再说一遍,copy_if
就是干这个的)
使用这些调整,查询 class 可以像这样完全实现:
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
完整演示
#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
}
此代码在崩溃之前有 运行 100,000 次(测试中 运行 宁 6 小时)。当崩溃发生时,它会在 shared_ptr 超出范围时发生......此函数的目的是填充传入的 shared_ptr 向量(在这种情况下),并按类型过滤消息。 ..所以并不是 historyQueue 中的所有消息都被添加。这个新填充的向量稍后用于发送这些 PM...这个函数也被其他线程调用,这就是为什么有一个 LockGuard,它是标准锁卫的类型定义(typedef std::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
) 上的竞赛。很容易忘记同步访问。
稍微跑题了:我可以建议稍微简化一下吗。
减少 code/separating 问题总是有助于保持复杂性,从而降低错误率:
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;
}
那里有很多 confusing/conflicting 东西:
条件
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
信号也设为 'no userid filter' 呢?什么是
for_vector
(确定...写循环不是宏?再说一遍,copy_if
就是干这个的)
使用这些调整,查询 class 可以像这样完全实现:
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
完整演示
#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
}