0

質問に適切なタイトルを付けることができなかった場合は申し訳ありません。非常に興味深いことに気付いたとき、私は自分のプログラムをデバッグしていました。コードは非常に簡単です。私のコメントをインラインでフォローしてください:

//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();
} 

誰が (で) 私の内部boost::asioコピーを作成し、いつそれらすべてをリリースするのかわかりません。shared_ptr

実際、次のような場合にこの状況に気付きました: アプリケーションが完了するまで実行され、ネストされたオブジェクトでいっぱいのコンテナーが (私ではなく自動的に) クリーンアップされているときに、プログラムがしようとしている場所で が呼び出されたshared_ptr後に、セグ フォールトが発生します。 ~Session()a に対処しますstd::vector<char> (これは、最初に覚えておくように言った場所です)。これは、Eclipseデバッガーで確認できました。

私はセグフォルトを読むのが苦手ですが、プログラムが存在しないものを試みてclearいると思います。vector

長い質問で申し訳ありませんが、お時間を割いていただき、親切なコメントに感謝します。

EDIT-1:Sessionアプリケーションを変更して、新しい(s) ではなく 生のポインターを使用するようにしましshared_ptrた セッションを削除しないと、セグフォルトはなくなります。したがって、少なくとも、セグフォルトの原因は Session にあると確信しています。

編集-2: 前回の更新で述べたように、セッションを削除しようとすると問題が発生しますが、セグメント障害につながるトレースが毎回異なります。時々これ:

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

そして時々これ:

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 

私の記憶はすでに壊れているということですか?さらにチェックするにはどうすればよいですか?ありがとうございました

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 には (オプションの) 2 番目のパラメーター (ここで使用している完了関数) があります。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 は をshared_ptr呼び出すまで (boost::bind を介して)あなたのコピーを保持しますhandle_accept。これにより、最初に を渡すことができますshared_ptr。引数の 1 つとして追加しなかった場合は、作成した関数にスコープが設定されるとすぐにオブジェクトがクリーンアップされます。

生のポインターを使用しても明らかにならない他の未定義の動作があると思われます。

于 2013-06-17T05:21:03.543 に答える
1

2 番目の質問に答える (しようとする): セッションで二重の削除を発行しているようです。これは、生のポインターから 2 番目の scoped_ptr を作成する場合にのみ可能です。これはやってはいけないことです。セッションへの生のポインターを、そのスコープ付き ptr を作成する関数に渡していますか?

セッションに enable_shared_from_this を継承させることができます。これにより、すべての raw ポインターが同じ scoped_ptr カウンターを使用するため、問題が修正されます。しかし、これを実際の修正と見なすべきではありません。実際の修正は、複数の scope_ptr インスタンス化を排除することです。

編集:別のデバッグの可能性を追加

セッションのデストラクタにブレークポイントを設定し、最初/2番目の削除のバックトレースを確認することもできます。

于 2013-06-17T10:39:41.173 に答える
1

この回答で説明されているように、Boost.Asio の async_* 関数で共有ポインターを使用しても問題ありません。


コール スタックと動作に基づいて、少なくとも 1 つのリソースが 2 回削除されているように見えます。Session生のポインターとの両方で管理されている可能性はありshared_ptrますか?

で管理boost::shared_ptr:

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

raw-pointer による管理:

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

ConnectionHandlerで管理Sessionしていた場合boost::shared_ptr、コール スタックは と表示されboost::shared_ptr<sim_mob::Session>::~shared_ptr()ます。shared_ptrまた、によってすでに管理されている生のポインタからを作成しないように注意しshared_ptrてください。これにより、shared_ptrがリソースを 2 つの異なるリソースとして管理し、二重の削除が行われることになります。

// 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

補足として、一般的なイディオムの 1 つは、継承元をSession継承することenable_shared_from_thisです。thisSessionの代わりに共有ポインターをインスタンスへのハンドルとして渡すことにより、非同期呼び出しチェーンの期間中、 を存続させることができます。たとえば、の結果がインスタンス ハンドルとしてコールバックにバインドされている限り、非同期の読み取り操作が未解決の間、 を存続させることができます。Sessionshared_from_this()Session::async_read

于 2013-06-17T17:14:54.287 に答える