Christopher Pisz <cpisz <at> austin.rr.com> writes:
I posted an async_accept to my io_service. The async_accept gets posted again to listen for incoming connections every time it completes.
I was under the impression that calling io_service::stop, would be the way to stop listening.
When debugging, I see that a function object was created when I called bind inside my async_accept call.
That function object contains a smart pointer to the instance of the object
to
whom the method to call back belongs.
Since the callback never happens, my instance never gets destroyed.
How do I effectively cancel any outstanding work the io_service has posted to it, such that the smart pointer in the function object that the bind created will lose its reference count?
In debugging, the callback never gets called after stop, not does the reference count in the smart pointer decrement.
------------------------------ Here is the sequence of events: void TcpListener::Listen() { // Bind function callbacks that the connection will communicate to us with // // Important: These cannot be shared pointers, because the bind call actually // creates an object that stores the pointer. Since these callbacks // are stored in the connection object, it would create a reference // count that would never go away // TcpServerSideConnection::AcceptCompleteListenerCallback acceptCallback = boost::bind(&TcpListener::OnAcceptComplete, this, boost::asio::placeholders::error, _2); TcpServerSideConnection::ConnectionClosedListenerCallback connectionClosedCallback = boost::bind(&TcpListener::OnConnectionClosed, this, _1); TcpServerSideConnection::ReadPayloadUTF8CompleteListenerCallback readPayloadUTF8Callback = boost::bind(&TcpListener::OnReadPayloadUTF8Complete, this, _1, _2); // Create a connection to be accepted TcpServerSideConnection::SmartPtr newConnection = TcpServerSideConnection::Create(ioService_, acceptCallback, connectionClosedCallback, readPayloadUTF8Callback); // Let the connection itself issue the async accept, so that it may manage its internal state. // It will call us back if successfully accepted a connection request newConnection->Accept(acceptor_); } void TcpServerSideConnection::Accept(boost::asio::ip::tcp::acceptor & acceptor) { // Close if open Close(); // Change state to reflect are posting the request to connect for io completion state_ = CONNECTING; // Make the async call TcpServerSideConnection::SmartPtr me = boost::dynamic_pointer_cast<TcpServerSideConnection, TcpBaseSocket> (shared_from_this()); AcceptCompleteCallback callback = boost::bind (&TcpServerSideConnection::OnAcceptComplete, me, boost::asio::placeholders::error); // HERE IS MY PROBLEM!!!! callback never destroys!!! acceptor.async_accept(socket_, callback); } void TcpServerSideConnection::OnAcceptComplete(const boost::system::error_code & error) { if( error ) { // It will be up to the listener to handle the error // There is nothing we can really do } else { // Successfully connected state_ = CONNECTED; } // Notify the TcpListener that the accept attempt has been completed // Pass a smart pointer to ourself and leave it to the listener to keep us alive if( acceptCompleteListenerCallback_ != 0 ) { TcpServerSideConnection::SmartPtr me = boost::dynamic_pointer_cast<TcpServerSideConnection, TcpBaseSocket> (shared_from_this()); acceptCompleteListenerCallback_(me, error); } // Post receive right away, such that we receive data when it comes ReadHeader(); } void TcpListener::OnAcceptComplete(TcpServerSideConnection::SmartPtr newConnection, const boost::system::error_code & error) { // Issue another listen for the next connection request Listen(); // Check if any errors occured if( error ) { /// Let the connection destroy itself return; } // This class does not actualy do anything with the connection // It is up to derived classes to store and use the connection } TcpListener::~TcpListener() { // Destroying the boost work object will allow the io_service run method to exit // when it has no more work queued for completion. when the io_service run method exits, // the io service thread will also exit ioServiceAlive_.reset(); // Wait for the io service thread to exit // Attempting to handle any remaining work the io service had to do // We will give it a maximum of 2 seconds, which is just an arbitrary amount of time if( !ioServiceThread_.timed_join(boost::posix_time::seconds(2)) ) { // The thread did not exit gracefully // Ditch any pending work in an attempt to get the thread to exit // // THIS DOES NOT SEEM TO KILL THE OUTSTANDING ACCEPT CALL!!! ioService_->stop(); // We'll wait one more second if ( !ioServiceThread_.timed_join(boost::posix_time::seconds(1)) ) { // There does not exit a way to force it without getting the native handle // This should never happen // TODO - Error - handle it...probably log it and keep going } } }