简体   繁体   English

在boost :: asio异步模式下创建和接受套接字时,我可以使用boost :: shared_ptr吗?

[英]can I use a boost::shared_ptr when creating&accepting a socket in boost::asio async mode?

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. 我不知道谁(在boost::asio )在内部复制我的shared_ptr以及何时将其全部释放。

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). 实际上,我在以下情况下注意到了这种情况:我的应用程序运行完毕,并且在清理充满嵌套的shared_ptr ed对象的容器时(自动,不是我自己),在〜Session ~Session()调用后出现段错误程序试图处理std::vector<char> (这是我一开始告诉您的地方)。 I could see this through eclipse debugger. 我可以通过Eclipse调试器看到这一点。

I am not good in reading seg faults but I guess the program is trying to clear a vector that doesn't exist. 我不擅长读取段错误,但我猜程序正在尝试clear不存在的vector

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 . EDIT-1:我刚刚修改了我的应用程序,以使用原始指针创建新的Session而不是shared_ptr The seg fault is gone if I dont delete the Session. 如果我不删除会话,seg错误就消失了。 So at least I am sure the cause of the seg fault is in Session . 因此,至少我确定seg错误的原因在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. EDIT-2:正如我在先前的更新中提到的那样,当我尝试删除会话时出现问题,但是每次导致seg错误的跟踪都不同。 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 谢谢

This line is where the magic lives: 这行是魔术的来源:

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

The async_accept has an (optional) second parameter - a completion function which you are using here. async_accept具有(可选)第二个参数-您在此处使用的完成函数。 You are using boost::bind to create a functor that matches the completion function declaration. 您正在使用boost :: bind创建与完成函数声明匹配的函子。 You are passing a new_sess smart pointer to that handler (this is why the smart_pointer is not deleted when you leave the scope). 您正在将new_sess智能指针传递给该处理程序(这就是为什么离开范围时不会删除smart_pointer的原因)。

In other words: The async_accept function takes either a functor with no parameters or a functor that accepts an error. 换句话说:async_accept函数采用不带参数的函子或接受错误的函子。 You may now create a class that overloads the operator() with that signature. 您现在可以创建一个用该签名重载operator()的类。 Instead you use boost::bind. 相反,您使用boost :: bind。 Boost::bind allows you to either provide the parameters when the (inner) function is called or when constructing the functor by calling boost::bind. Boost :: bind允许您在调用(内部)函数时或在通过调用boost :: bind构造函子时提供参数。 You provided some parameters when calling boost::bind - the smart pointer to the session. 调用boost :: bind-会话的智能指针时,您提供了一些参数。

This is a common pattern with boost::asio. 这是boost :: asio的常见模式。 You pass your context to the asynchronous function. 您将上下文传递给异步函数。 When this function detects an error all you need to do is to leave the function. 当此功能检测到错误时,您要做的就是离开该功能。 The context then leaves the scope and will be deleted. 然后,上下文离开范围,将被删除。 When no error is detected you pass the context (via boost::bind) to the next async function and the context will be kept alive. 如果未检测到错误,则将上下文(通过boost :: bind)传递给下一个异步函数,该上下文将保持活动状态。

You should be able to use shared_ptr in that way, I use it in the same manner without issue. 您应该能够以这种方式使用shared_ptr ,我将以相同的方式使用它而不会出现问题。

Internally, asio keeps a copy of your shared_ptr (via boost::bind) until it calls handle_accept . 在内部,asio会保留您shared_ptr的副本(通过boost :: bind),直到调用handle_accept This is what allows you to pass the shared_ptr to begin with. 这就是允许您传递shared_ptr的开始。 If you did not add it as one of the arguments, then it would clean up the object as soon as it scoped in the function you created it. 如果您未将其添加为参数之一,则它将在对象位于您创建的函数中后立即对其进行清理。

I suspect that you have other undefined behavior that using a raw pointer with does not uncover. 我怀疑您还有其他未定义的行为,使用原始指针无法发现。

To (try to) answer your second question: It seems like a you are issuing a double delete on the session. 要(尝试)回答您的第二个问题:似乎您正在对该会话发出两次删除。 This is only possible if you create a second scoped_ptr from a raw pointer. 仅当您从原始指针创建第二个scoped_ptr时,才有可能。 This is something you shouldn't do. 这是你不应该做的。 Are you passing a raw pointer to session to any function that in turn creates a scoped ptr of it? 您是否将指向会话的原始指针传递给任何反过来又会为其创建作用域ptr的函数?

You could try to let Session inherit enable_shared_from_this. 您可以尝试让Session继承enable_shared_from_this。 This will fix the problem as any raw pointer uses the same scoped_ptr counter. 这将解决此问题,因为任何原始指针都使用相同的scoped_ptr计数器。 But you should not see this as a real fix. 但是您不应该将此视为真正的解决方案。 The real fix would be to eliminate the multiple scope_ptr instanciations. 真正的解决方法是消除多个scope_ptr实例。

Edit: Added another debug possibility 编辑:添加了另一种调试可能性

Something else you could try would be to set a breakpoint in the destructor of the session and see the backtrace of the first/second delete. 您可以尝试的其他方法是在会话的析构函数中设置一个断点,并查看第一次/第二次删除的回溯。

As covered in this answer , it is fine to use shared pointers with Boost.Asio's async_* functions. 如本回答所述 ,可以将共享指针与Boost.Asio的async_ *函数一起使用。


Based on the call stacks and behavior, it looks as though at least one resource is being deleted twice. 根据调用堆栈和行为,看起来好像至少一个资源被删除了两次。 Is it possible that Session is being managed through both a raw pointer and a shared_ptr ? 是否有可能同时通过原始指针和shared_ptr管理Session

Managing with boost::shared_ptr : 使用boost::shared_ptr管理:

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

Managing with raw-pointer: 使用原始指针进行管理:

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

If ConnectionHandler was managing Session with boost::shared_ptr , then the call stack should show boost::shared_ptr<sim_mob::Session>::~shared_ptr() . 如果ConnectionHandler正在使用boost::shared_ptr管理Session ,则调用堆栈应显示boost::shared_ptr<sim_mob::Session>::~shared_ptr() Also, be careful not to create a shared_ptr from a raw pointer that is already being managed by a shared_ptr , as it will result in the shared_ptr s managing the resource as two distinct resources, resulting in a double deletion: 此外,要小心,不要创建shared_ptr从已经被托管的原始指针shared_ptr ,因为它会导致shared_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

As a side note, one common idiom is to have Session inherit from enable_shared_from_this . 附带说明一下,一种常见的习惯用法是让Sessionenable_shared_from_this继承。 It allows for Session to remain alive throughout the duration of its asynchronous call chains by passing the shared pointer as a handle to the instance in place of this . 它允许Session通过传递共享指针的句柄实例代替整个异步调用链的持续时间仍然活着。 For example, it would allow for Session to remain alive while an asynchronous read operation is outstanding, as long as the result of shared_from_this() is bound as the instance handle to the Session::async_read callback. 例如,只要将Session::async_read shared_from_this()的结果绑定为Session::async_read回调的实例句柄,这将允许Session在未完成异步读取操作时保持活动Session::async_read

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM