On 31/01/2018 20:00, Stian Zeljko Vrba wrote:
And this what the C++11 HTTP server example does to abort a read/write loop for the client:
void connection::stop() { socket_.close(); }
Exactly what I wanted to avoid. Why?
The mundane reason is that I want to have clean event logs. This is how the example(s) you're referring to "handle" errors (an excerpt):
... else if (ec != boost::asio::error::operation_aborted) { connection_manager_.stop(shared_from_this()); } ...
The principled reason ist that I'd like that error code != 0 really means that a hard error happened, instead of "maybe error, maybe my program wants to abort the handler loop". With the example "solution" I'd be de-facto repurposing errc::invalid_handle (or whatever it's called) to mean the same as errc::operation_aborted, which I don't like at all. If I have to explain why: because I want all the help I can get from the OS to diagnose my own mess-ups.
If you close the socket while an operation is pending, it will trigger operation_aborted, which should not be logged (as this is an "expected" error). It will also *not start another operation* (for any error). So you will never see an invalid_handle error as a result of that as long as some operation is pending at all times. Thus invalid_handle is always an actual error. What about the case when a read has completed? This is not a problem as long as you make sure that you post the (method that calls) close() to the io_service, ensuring that it is on the same strand as the read (or any other operations) -- which is automatic if you only have one worker thread. Why? Because the completion handler of the read operation will always start another read operation if it succeeds, and never do so if it fails. And posted tasks are always executed in the order posted (when there is one worker thread, or they are posted through the same strand). So either the read is pending, and the close/cancel will abort it and give you an operation_aborted; or the read is completed, and the read's completion handler will execute and start a new read before the close/cancel can execute. What you *don't* want to do is to execute the close/cancel in some other context, as then you'll get weirder behaviour without some other mechanism (such as an extra flag) to track that you're trying to shut down.