Boost logo

Boost Users :

Subject: Re: [Boost-users] boost::asio::io_service - cancel posted work
From: Christopher Pisz (cpisz_at_[hidden])
Date: 2011-10-05 18:43:20


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


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net