Boost logo

Boost Users :

Subject: Re: [Boost-users] asio: cancelling a named pipe client
From: Richard Hodges (hodges.r_at_[hidden])
Date: 2018-01-31 02:00:13

> I guess this semi-rant can be summarized as: asio needs documentation on
best practices/patterns/guidelines for life-time management of handlers.

With this I agree. It has taken me quite some time to become familiar
enough with asio to be able to write correct services for it. I had to read
the (sparse) documentation a number of times, and made numerous mistakes.

On 30 January 2018 at 22:08, Gavin Lambert via Boost-users <
boost-users_at_[hidden]> wrote:

> On 31/01/2018 03:03, Stian Zeljko Vrba wrote:
>> > It is not valid to have more than one outstanding async read on an
>> asio io object at a time
>> Although I'm not doing this, this restriction is mentioned in the
>> documentation only for composite operations (free-standing async_read) but
>> not for members on objects (async_read_some()).
> There is actually no technical restriction from having multiple pending
> reads -- even for standalone async_read. You can do it, and it will behave
> "correctly".
> The trouble is that this correct behaviour is not *useful* behaviour. If
> you have multiple pending reads on the same object then it means the OS is
> free to scatter the data some here, some there, and it becomes impossible
> to make sense of the data arrival order, which renders stream sockets
> fairly useless. (It *is* something you can sensibly do with message-based
> sockets, though -- but it's still unusual because application-layer
> protocols usually aren't written to react well to things being processed
> out of expected order.)
> Multiple writes are the same -- there's no technical reason why you can't,
> but usually it's nonsensical to actually do it since the data can end up
> interleaved in strange and unexpected ways at the other end.
> So the limit of one outstanding read and one outstanding write at a time
> is a practical one, not a technical one.
> So asio leaves handling of difficult (edge-)cases to all users instead of
>> offering a user-friendly opt-in solution, such as: each i/o object tracks
>> posted, but not-yet-executed handlers. When cancel on the object is called,
>> it would traverse the list and update error codes.
> There is no list to traverse. There can't be, due to the nature of MPMC
> queues.
> Besides, if an operation did actually execute correctly, it's usually more
> useful to report that success even if a cancel occurred later -- after all,
> the bytes were actually read or transmitted, and it may be important to
> know that so that you know what you need to send next.
> (Unrelated: individual operations cannot be canceled (e.g., read, but not
>> write); this is a glaring design omission from my POV. I needed that in
>> another project.)
> This is generally an OS limitation.
> It's also very standard in concurrent programming that requests to cancel
> are just that: requests. The request is free to be ignored if the task has
> already completed, even if the callback hasn't been invoked yet, and
> especially if the callback might already be executing.
> It's simply not possible to do it any other way.
> Yes, that's another gotcha when you have outstanding both reads and
>> writes. Esp. tricky to discover and fix when only, say, read, fails due to
>> broken pipe, but there's no data to send so that also write() fails in the
>> forseeable future. Then io_service just sits and hangs there waiting for
>> the write handler to return...
> If there's no data to write then you don't have a pending write to begin
> with. Write operations are only started when you actually have data to
> send, and typically complete very quickly (with the exception of pipes that
> are full) -- typically only read (and listen) operations are left pending
> for long periods while waiting for incoming data.
> .. I guess this semi-rant can be summarized as: asio needs documentation
>> on best practices/patterns/guidelines for life-time management of handlers.
> It has examples.
> Does weak_ptr protect against an analogue of the "ABA" problem: Say I have
>> a permanent weak_ptr to an alive shared_ptr. Then the shared_ptr gets
>> destroyed. Then another shared_ptr of the same type gets created, but both
>> the object and the control block get the same addresses as the previous
>> instances (not unthinkable with caching allocators). How will lock() on the
>> existing weak_ptr behave? Intuitively, it should return null, but will it?
>> Does the standard say anything about this?
> Yes, it will reliably return nullptr. It is not possible for a new object
> to have the same control block address as some prior object as long as any
> weak_ptrs to the original object exist.
> Essentially both the control block and the object are refcounted; a
> shared_ptr holds a count of both the object and the control block, while a
> weak_ptr holds a count of the control block alone.
> _______________________________________________
> Boost-users mailing list
> Boost-users_at_[hidden]

Boost-users list run by williamkempf at, kalb at, bjorn.karlsson at, gregod at, wekempf at