|
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