0

Sorry if I wasn't able to put a better title to my question. I was debugging my program when I noticed something very interesting. The code is very straightforward. please follow my comments inline:

//my session class
class Session
{
public:
  /// Constructor.
  Session(boost::asio::io_service &io_service)
    : socket_(io_service)
  {
  }

  boost::asio::ip::tcp::socket& socket()
  {
    return socket_;
  }

void async_read(/*...*/);
void async_write(/*...*/);
//blah blah
private:
std::vector<char> inbound_data_;//<---note this variable, but don't mind it until i tell you
std::string outbound_data_;
boost::asio::ip::tcp::socket socket_;
}

typedef boost::shared_ptr<Session> session_ptr; //just for easy reading


//and this is my connection server class
class ConnectionServer {
public:
void ConnectionServer::CreatSocketAndAccept() {
    session_ptr new_sess(new Session(io_service_));//<--I created a scope limited shared_ptr
    Print()<< "new_sess.use_count()= " << new_sess.use_count() << std::endl;//prints 1
    acceptor_.async_accept(new_sess->socket(),//<-used it for async connection acceptance
            boost::bind(&ConnectionServer::handle_accept, this,
                    boost::asio::placeholders::error, new_sess));
   Print()<< "new_sess.use_count()= " << new_sess.use_count() << std::endl;//prints 2
}//<-- Scope is ending. what happens to my new_sess? who keeps a copy of my session?

//and now the strangest thing:
void ConnectionServer::handle_accept(const boost::system::error_code& e, session_ptr sess) {
    if (!e) {

        Print()<< "sess.use_count()= " << sess.use_count() << std::endl;//prints 4 !!!! while I have never copied the session anywhere else in between
        Print() << "Connection Accepted" << std::endl;
        handleNewClient(sess);
    }
    else
    {
        std::cout << "Connection Refused" << std::endl;
    }
    CreatSocketAndAccept();
} 

I don't know who(in boost::asio) copies my shared_ptr internally and when it is going to release them all.

In fact, I noticed this situation when: My application runs to completion and at the time when containers full of nested shared_ptr ed objects are being cleaned up(automatically and not by me), I get a seg fault after ~Session() is called where program is trying to deal with a std::vector<char> (this is where I told you to remember in the beginning). I could see this through eclipse debugger.

I am not good in reading seg faults but I guess the program is trying to clear a vector that doesn't exist.

Sorry for the long question I value your time and appreciate your kind comments.

EDIT-1: I just modified my application to use raw pointers for creating new Session(s) rather than shared_ptr. The seg fault is gone if I dont delete the Session. So at least I am sure the cause of the seg fault is in Session .

EDIT-2: As I mentioned in my previous update, the problem occurs when I try to delete the session but every time the trace leading to the seg fault is different. sometimes this:

Basic Debug [C/C++ Application] 
    SimMobility_Short [10350] [cores: 0]    
        Thread [1] 10350 [core: 0] (Suspended : Signal : SIGSEGV:Segmentation fault)    
            malloc_consolidate() at malloc.c:4,246 0x7ffff5870e20   
            malloc_consolidate() at malloc.c:4,215 0x7ffff5871b19   
            _int_free() at malloc.c:4,146 0x7ffff5871b19    
            __gnu_cxx::new_allocator<char>::deallocate() at new_allocator.h:100 0xa4ab4a    
            std::_Vector_base<char, std::allocator<char> >::_M_deallocate() at stl_vector.h:175 0xab9508    
            std::_Vector_base<char, std::allocator<char> >::~_Vector_base() at stl_vector.h:161 0xabf8c7    
            std::vector<char, std::allocator<char> >::~vector() at stl_vector.h:404 0xabeca4    
            sim_mob::Session::~Session() at Session.hpp:35 0xabea8d 
            safe_delete_item<sim_mob::Session>() at LangHelpers.hpp:136 0xabef31    
            sim_mob::ConnectionHandler::~ConnectionHandler() at ConnectionHandler.cpp:40 0xabd7e6   
            <...more frames...> 
    gdb

and some times this:

Basic Debug [C/C++ Application] 
    SimMobility_Short [10498] [cores: 1]    
        Thread [1] 10498 [core: 1] (Suspended : Signal : SIGSEGV:Segmentation fault)    
            _int_free() at malloc.c:4,076 0x7ffff5871674    
            std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() at 0x7ffff639d540   
            sim_mob::ConnectionHandler::~ConnectionHandler() at ConnectionHandler.cpp:30 0xabd806   
            boost::checked_delete<sim_mob::ConnectionHandler>() at checked_delete.hpp:34 0xadd482   
            boost::detail::sp_counted_impl_p<sim_mob::ConnectionHandler>::dispose() at sp_counted_impl.hpp:78 0xadd6a2  
            boost::detail::sp_counted_base::release() at sp_counted_base_gcc_x86.hpp:145 0x849d5e   
            boost::detail::shared_count::~shared_count() at shared_count.hpp:305 0x849dd7   
            boost::shared_ptr<sim_mob::ConnectionHandler>::~shared_ptr() at shared_ptr.hpp:164 0x84a668 
            sim_mob::ClientHandler::~ClientHandler() at ClientHandler.cpp:42 0xac726d   
            sim_mob::ClientHandler::~ClientHandler() at ClientHandler.cpp:45 0xac72da   
            <...more frames...> 
    gdb 

does it mean my memory is already corrupted? How can I do more checks? Thank you

4

4 回答 4

2

这条线是魔法所在:

acceptor_.async_accept(new_sess->socket(),//<-used it for async connection acceptance
        boost::bind(&ConnectionServer::handle_accept, this,
                boost::asio::placeholders::error, new_sess));

async_accept 有一个(可选的)第二个参数 - 您在此处使用的完成函数。您正在使用 boost::bind 创建一个与完成函数声明匹配的函子。您正在将一个 new_sess 智能指针传递给该处理程序(这就是为什么在您离开范围时不会删除 smart_pointer 的原因)。

换句话说: async_accept 函数要么接受一个没有参数的仿函数,要么接受一个接受错误的仿函数。您现在可以创建一个使用该签名重载 operator() 的类。相反,您使用 boost::bind。Boost::bind 允许您在调用(内部)函数时或在通过调用 boost::bind 构造函子时提供参数。您在调用 boost::bind 时提供了一些参数 - 指向会话的智能指针。

这是 boost::asio 的常见模式。您将上下文传递给异步函数。当此函数检测到错误时,您需要做的就是离开该函数。然后上下文离开范围并将被删除。当没有检测到错误时,您将上下文(通过 boost::bind)传递给下一个异步函数,并且上下文将保持活动状态。

于 2013-06-17T05:20:51.827 回答
2

你应该能够以shared_ptr这种方式使用,我以同样的方式使用它没有问题。

在内部,asio 会保留您(通过 boost::bind)的副本,shared_ptr直到它调用handle_accept. 这就是让你通过的shared_ptr开始。如果您没有将其添加为参数之一,那么它会在对象限定在您创建它的函数时立即清理它。

我怀疑您还有其他未定义的行为,使用原始指针不会发现。

于 2013-06-17T05:21:03.543 回答
1

要(尝试)回答您的第二个问题:您似乎在会话上发出双重删除。这只有在您从原始指针创建第二个 scoped_ptr 时才有可能。这是你不应该做的事情。您是否将指向 session 的原始指针传递给任何函数,进而创建它的作用域 ptr?

您可以尝试让 Session 继承 enable_shared_from_this。这将解决问题,因为任何原始指针都使用相同的 scoped_ptr 计数器。但是您不应该将此视为真正的解决方案。真正的解决方法是消除多个 scope_ptr 实例。

编辑:添加了另一种调试可能性

您可以尝试的其他方法是在会话的析构函数中设置断点并查看第一次/第二次删除的回溯。

于 2013-06-17T10:39:41.173 回答
1

如本答案所述,可以将共享指针与 Boost.Asio 的 async_* 函数一起使用。


根据调用堆栈和行为,看起来好像至少有一个资源被删除了两次。是否有可能Session同时通过原始指针和 a 进行管理shared_ptr

管理boost::shared_ptr

void ConnectionServer::CreatSocketAndAccept() {
  session_ptr new_sess(new Session(io_service_)); // shared pointer
  ...
}

使用原始指针进行管理:

sim_mob::Session::~Session()
safe_delete_item<sim_mob::Session>() // raw pointer
sim_mob::ConnectionHandler::~ConnectionHandler()

如果用ConnectionHandler管理,那么调用堆栈应该显示。此外,请注意不要从已由 a 管理的原始指针创建 a ,因为这将导致s 将资源作为两个不同的资源进行管理,从而导致双重删除:Sessionboost::shared_ptrboost::shared_ptr<sim_mob::Session>::~shared_ptr()shared_ptrshared_ptrshared_ptr

// p1 and p2 use the same reference count to manage the int.
boost::shared_ptr<int> p1(new int(42));
boost::shared_ptr<int> p2(p1); // good

// p3 uses a different reference count, causing int to be managed
// as if it was a different resource.
boost::shared_ptr<int> p3(p1.get()); // bad

作为旁注,一个常见的习惯用法是Session继承自enable_shared_from_thisSession通过将共享指针作为句柄传递给实例来代替this,它允许在其异步调用链的整个持续时间内保持活动状态。例如,它允许Session在异步读取操作未完成时保持活动状态,只要将结果shared_from_this()绑定为Session::async_read回调的实例句柄。

于 2013-06-17T17:14:54.287 回答