> 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).

The documentation for async_read_some contains the following ambiguous remark about immediate completion: "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_context::post(). "

So is the following possible:

  1. Another read operation is started, but that one completes immediately because data is ready on the pipe. I.e., async_read_some immediately posts the completion handler.
  2. close() has nothing to cancel, but closes the socket.
  3. The handler posted in 1 is executed, it goes to initiate another read, and that read finds the pipe closed, returning errc::invalid_handle.

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

Even if async_read_some does *not* post the handler immediately but just initiates the I/O, this I/O can complete immediately and cannot be canceled on the OS-level. Thus attempted cancellation by close() will be a noop, the read handler will get an OK status and proceed to use the closed handle...

According to the comment in the code in the link below, this is a real possibility: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363789(v=vs.85).aspx



From: Boost-users <boost-users-bounces@lists.boost.org> on behalf of Gavin Lambert via Boost-users <boost-users@lists.boost.org>
Sent: Wednesday, January 31, 2018 8:14:28 AM
To: boost-users@lists.boost.org
Cc: Gavin Lambert
Subject: Re: [Boost-users] asio: cancelling a named pipe client
 
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.

_______________________________________________
Boost-users mailing list
Boost-users@lists.boost.org
https://lists.boost.org/mailman/listinfo.cgi/boost-users