What happens to handlers when socket is deleted - c++

What happens to completion handlers when socket is shutdown/closed and then deleted (that is destructor is run and memory released)? AFAIK after socked it closed all completion handlers will receive error code next time even loop is polled. But what happens if the socked is deleted before even handlers had a chance to run? Is it OK to delete socket before dispatching event handlers?

It is safe to delete a socket before its outstanding handlers have been executed. Outstanding operations will have their handlers set to be invoked with boost::asio::error::operation_aborted. It is the responsibility of the application code to to make sure that the handlers do not invoke operations on the deleted socket.
For details, destroying an IO object, such a socket, will cause the IO object's service to be destroyed. The SocketService requirements state that destroy() will implicitly cancel asynchronous operations. Outstanding asynchronous operations will try to complete as soon as possible. This causes the handlers for cancelled operations to be passed the error code boost::asio::error::operation_aborted, and scheduled for deferred invocation within the io_service. These handlers are removed from the io_service if they are either invoked from a thread processing the event loop or the io_service is destroyed.

All handlers is guaranteed to be called. If socket was closed - handlers will be called with some error code.
Normally, you need to use this guarantee to control lifetime of your objects using boost::enable_shared_from_this.
class Writer : boost::enable_shared_from_this<Writer>
{
boost::asio::socket scoket_;
...
void StartWrite()
{
boost::asio::async_write(socket_,
boost::asio::buffer(buffer_, bytes_sent_),
boost::bind(&Writer::Handler, shared_from_this,
boost::asio::placeholders::error));
...
void Handler(boost::system::error_code const& err) {...}
With this approach, your socket object will outlive all pending handlers.

Related

Which thread async operations take place

Afte reading the asio's documentation, it's clear to me that the completion handlers are called by one of the threads that called the io_service's io.run() method. However, something that's is not clear to me is which thread the read/write async methods take place. Is it the thread that I call the methods or is it in one of the threads that called the io.run() method? Or, in last case, does the library create another thread behind the scenes and performs the operation?
The I/O operation will be attempted within the initiating async_* function. If either the operation's completion condition is satisfied or an error occurs, then the operation is complete and the completion handler will be posted into the io_service. Otherwise, the operation is not complete, and it will be enqueued into the io_service, where an application thread running the io_service's function poll(), poll_one(), run(), or run_one() performs the underlying I/O operation. In both cases, the completion handler is invoked by a thread processing the io_service.
The async_write() documentation notes that the asynchronous operation may be completed immediately:
Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using boost::asio::io_service::post().
This behavior is also noted in the Requirements on Asynchronous Operations documentation:
When an asynchronous operation is complete, the handler for the operation will be invoked as if by:
Constructing a bound completion handler bch for the handler ...
Calling ios.post(bch) to schedule the handler for deferred invocation ...
This implies that the handler must not be called directly from within the initiating function, even if the asynchronous operation completes immediately.
Here is a complete example demonstrating this behavior. In it, socket1 and socket2 are connected. Initially, socket2 has no data available. However, after invoking async_write(socket1, ...), socket2 has data even though the io_service has not been ran:
#include <boost/asio.hpp>
constexpr auto noop = [](auto&& ...){};
int main()
{
using boost::asio::ip::tcp;
boost::asio::io_service io_service;
// Create all I/O objects.
tcp::acceptor acceptor{io_service, {{}, 0}};
tcp::socket socket1{io_service};
tcp::socket socket2{io_service};
// Connect sockets.
acceptor.async_accept(socket1, noop);
socket2.async_connect(acceptor.local_endpoint(), noop);
io_service.run();
io_service.reset();
// Verify socket2 has no data.
assert(0 == socket2.available());
// Initiate an asynchronous write. However, do not run
// the `io_service`.
std::string data{"example"};
async_write(socket1, boost::asio::buffer(data), noop);
// Verify socket2 has data.
assert(0 < socket2.available());
}
For instance, you want to send some data to a remote-partner - asynchronous.
boost::asio::async_write(_socket, boost::asio::buffer(msg.data(), msg.size()),
std::bind(&Socket::WriteHandlerInternal, this->shared_from_this(), std::placeholders::_1, std::placeholders::_2));
//Where 'this' is the class Socket
Before that, you probably have created a thread which called ioService.run(). The async_write function will take the same ioService you have used to create your socket. It puts it into the queue of your ioService to execute the write operation and the handler - on the thread your ioService runs on, as the async_ already suggests.

Are ASIO completion handlers invoked through the strand for cancelled operations?

Say there's a pending asynchronous operation with its completion handler wrapped by a strand when it is cancelled - for instance by closing a socket, cancelling a timer etc.
So, as I see it, the completion handlers will be enqueued with the error code operation_aborted. Now they can be dequeued by the io_service to be dispatched.
Is the way I'm telling this story right? If so, when the io_service invokes the completion handler, does it do through the strand even if they result from cancelled operations?
Yes, absolutely. It is an invariant that every asynchronous operation that is started completes. Regardless of the error code or success, the completion handler is executed the same way -- if it's strand wrapped, the handler will execute on the strand.
Typically you don't need to do anything in this case and the handler just checks for operation_aborted and returns. But if you want to do anything, you can. Also, the destruction of the callback object may cause things to happen. For example, if the invocation of the completion handler was through a shared_ptr, the destruction of that shared_ptr may trigger other destructors to run.

Do boost asio sockets have proper RAII cleanup

I tried looking through source but I cant navigate that much of a template code.
Basically: this is what documentation says (for close()):
Remarks
For portable behaviour with respect to graceful
closure of a connected socket, call shutdown() before closing the socket.
I can do that manually, but if possible it would be nice to rely on RAII.
So if I have socket going out of scope do I need to call shutdown() and close() on it, or it will be done automatically?
One can rely on the socket performing proper cleanup with RAII.
When an IO object, such as socket, is destroyed, its destructor will invoke destroy() on the IO object's service, passing in an instance of the implementation_type on which the IO object's service will operate. The SocketService requirements state that destroy() will implicitly cancel asynchronous operations as-if by calling the close() on the service, which has a post condition that is_open() returns false. Furthermore, the service's close() will cause outstanding asynchronous operations to complete as soon as possible. Handlers for cancelled operations will be passed the error code boost::asio::error::operation_aborted, and scheduled for deferred invocation within the io_service. These handlers are removed from the io_service if they are either invoked from a thread processing the event loop or the io_service is destroyed.

Boost::asio::async_write, handler called only once

I am quite new in boost::asio and I have a problem. I am writting client that sends in loop some commands to server. I am sending command with boost::asio::async_write and I expect that every time I send commands handler will be called. In fact only during first sending I see that handler is called. My client looks like that:
Client::Client(boost::asio::io_service & p_ioService,
boost::asio::ip::tcp::endpoint p_endpoint)
: io_service(p_ioService), endpoint(p_endpoint), socket(p_ioService)
{
socket.connect(endpoint);
}
Client::~Client()
{
socket.close();
}
void Client::sendCommand(const string & p_command)
{
boost::asio::async_write(socket,boost::asio::buffer(p_command),
boost::bind(&Client::onSendingFinished,this, _1, _2));
io_service.run();
}
void Client::onSendingFinished(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
cout<<"Sent "<<bytes_transferred<<endl;
}
There is no other place in main.cpp where io_service.run is called. I notice that if I call
io_service.reset() after io_service.run() it works fine, handler is called every time.
How should I solve this without io_service.reset()
Thanks in advance
I do not understand the aversion to calling io_service::reset(). In this case, it is necessary to invoke prior to any subsequent calls to io_service::run():
reset() must be called prior to any second or later set of invocations of the run(), run_one(), poll() or poll_one() functions when a previous invocation of these functions returned due to the io_service being stopped or running out of work.
It is possible that a thread returns from run() as a result of an exception being thrown, yet the io_service has neither been stopped nor ran out of work. In this case, the thread can invoke run() without calling reset().
The current Client::sendCommand() is synchronous. It is an implementation detail that it initiates an asynchronous operation, then blocks in io_service::run() waiting for the operation to complete. Unless there are multiple threads invoking commands on socket, multiple threads running the io_service, or the write operation needs to be cancellable, such as from a timeout, then it would be functionally equivalent and possible easier to implement Client::sendCommand() with a synchronous write().
void Client::sendCommand(const string & p_command)
{
boost::system::error_code ec;
std::size_t bytes_transferred =
boost::asio::write(socket, boost::asio::buffer(p_command), ec);
onSendingFinished(ec, bytes_transferred);
}
If Client::sendCommand() needs to be asynchronous, then:
The io_service should be ran from outside of Client::sendCommand(). If the io_service does not always have outstanding work, then io_service::work can be used control when run() returns. See this answer for more details as to when io_service::run() blocks and unblocks.
The underlying memory provided to async_write() as the buffer (p_command) needs to remain valid until the operation's handler, Client::onSendingFinished(), has been called. In this case, it may require making a copy of p_command in Client::sendCommand(), writing the copy to the socket, then deleting the copy from within the handler.
[...] ownership of the underlying memory blocks is retained by the caller, which must guarantee that they remain valid until the handler is called.
While it is not inherently bad to call reset() every now and then, there are two typical ways to avoid having to do it.
Start a new async operation within the handler of the first one. run() only returns once all handlers have finished and thus a new async operation started in a handler is still in time to keep blocking io_service.
Use io_service::work. If you create an instance of io_service::work constructed with your io_service as parameter, than your subsequent calls to run() will not return as long as the work object remains alive. And thus you will not have to reset anything. Of course this means that either one of your handlers or another thread has to destroy the work object at some time, if you want run() to ever stop blocking.
It's quite unusual to just send messages, it's far more common to have two way communication.
If you implemented a receiver as well, then your receive code would always require a receive handler running in the io_service and you wouldn't have this problem...

Clear boost::asio::io_service after stop()

I am using (single threaded) a boost::asio:io_service to handle a lot of tcp connections. For each connection I use a deadline_timer to catch timeouts. If any of the connections times out, I can use none of the results of the other connections. Therefore I want to completely restart my io_service. I thought that calling io_service.stop() would allow "finished" handlers in the queue to be called and would call handlers in the queue with an error.
However it looks like the handlers remain in the queue and therefore calling io_service.reset() and later io_service.run() brings the old handlers back up.
Can anyone confirm that the handlers indeed remain in the queue even after io_service.stop() is called. And if so, what are the possibilities to completly reset the io_service, e.g. remove all queued handlers?
io_service::stop() and io_service::reset() only control the state of the io_service's event loop; neither affect the lifespan of handlers scheduled for deferred invocation (ready-to-run) or user-defined handler objects.
The destructor for io_service will cause all outstanding handlers to be destroyed:
Each service object associated with the io_service will have its shutdown_service() member function invoked. Per the Service type requirement, the shutdown_service() member function will destroy all copies of user-defined handler objects that are held by the service.
Uninvoked handler objects scheduled for deferred invocation are destroyed for the io_service and any of its strands.
Consider either:
Controlling the lifespan of the io_service object. One approach can be found in this answer.
Running the io_service to completion. This often requires setting state, cancelling outstanding operations, and preventing completion handlers from posting additional work into the io_service. Boost.Asio provides an official timeout example, and a timeout approach with running to the io_service to completion is also shown here.