Boost logo

Boost :

From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2023-09-23 10:55:31


pt., 22 wrz 2023 o 17:02 Klemens Morgenstern <
klemensdavidmorgenstern_at_[hidden]> napisał(a):

> On Fri, Sep 22, 2023 at 10:34 PM Andrzej Krzemienski via Boost
> <boost_at_[hidden]> wrote:
> >
> > Hi Everyone,
> > In the context of the Boost review of Boost.Async, I wanted to share a
> > thought on documentation.
> > I think the Reference section in the docs is insufficient. A lot of
> > libraries in Boost have this approach that the reference section of the
> > documentation is a specification exhaustive enough that it could be used
> to
> > provide a number of competing implementations. Each function has a very
> > detailed contract: what it returns and when, what are the preconditions,
> > what exceptions are thrown upon failure, what are the postconditions. I
> am
> > missing this from the reference of Boost.Async.
>
> Post-conditions are not really a good fit for asynchronous code I
> fear, especially since a user can omit a co_await.
> You'd end up with a confusing mess IMO.

I agree with this statement. (BTW, we have an entire paper on this:
wg21.link/P2957)

Yet, I still maintain that this documentation is lacking a description of
what it expects and what it guarantees, even if you cannot call it "a
postcondition on the function". Consider the specs for `select` as an
example:

https://klemens.dev/async/reference.html#select

After reading it I am not sure I know what select() does, especially in the
corner cases. I suppose that if I were familiar with other async frameworks
from node.js or python I would know the answer.

I am sure you want to give me a *guarantee* of some sort; maybe not on the
call to select alone, but on the expression `co_await select(args...)`. So,
what is it?
The effect would be as if I called co_await on one (but it is not specified
which) of the awaitables from `args...`. Did I guess that right?

What happens when I call `co_await select(args...)` and all the awaitables
in `args...` have already been co_awaited on?

What happens if I pass zero arguments to `co_await select(args...)`?

What happens if I pass an empty vector to `co_await select(args...)`?

How do the results change if I pass a random number generator?

How does the state of non-selected awaitables change after the call to
`co_await select(args...)`?

If the answer to any of these questions is "you mustn't do that", this
should be listed as a precondition.

But I might add, that I don't
> like to read this kind of documentation either.
>
> >
> > Also, it may be one of the first libraries (that meet a certain bar of
> > documentation) tied so much to coroutines, so I have no strict
> requirements
> > on how a coroutine library is documented, but I think things like promise
> > types should be explicitly referenced.
>
> I did this by having base types for each promise which are indeed
> documented
>
> https://klemens.dev/async/reference.html#generator-promise

What I am missing is a section in Design part of documentation that says
that a number of features are implemented as base classes that
promise-types are expected to derive from.

For a feature like "cancellation" or "cancellation state" I would expect a
synopsis of class `promise_throw_if_cancelled_base` along with its function
`await_transform` and the description of what the function does.

>
> >
> > Let's consider `async::generator`. It is not only class template
> > `generator`, but also:
> >
> > 1. the specialization of type trait std::coroutine_traits
>
> It doesn't have that specialization.
>

My bad.
But it has a public alias promise_type which has an effect on what
guarantees I get.

> > 2. Class template `generator_promise`
>
> > 3. generator_promise
>
> Not directly documented, but it's properties are listed
>
> See here https://klemens.dev/async/reference.html#generator-promise

Yes, but I feel the docs should say it in a bit more detail. Like that it
is the function `await_transform` of a specific class that does this or
that. So that I can predict what is going to happen in my program.

>
> > 4. generator_receiver
>
> Why would this need to be documented? That's clearly an implementation
> detail.
>

You may be right here. I am having difficult times figuring out what is and
what is not an implementation detail in the case of coroutine-based
libraries.

>
> > 5. awaitable types
>
> Documented as a concept here:
> https://klemens.dev/async/reference.html#awaitable
>
> >
> > Even though some of them belong to namespace `detail`, they provide
> > guarantees relevant for the users. I do not think `generator_promise`is
> an
> > implementation detail. I think it needs to be documented. This seems
> > especially important when I start adding my awaitables to the mix.
>
> It shouldn't be. The recommended way is to check for associators
> through concepts, as describe here:
>
> https://klemens.dev/async/design.html#associators

I do not know what to make of this. I am not well familiar with associators
yet. The docs says "async uses the associator concept of asio, but
simplifies it." I read it as saying that one cannot add one's own
awaitables until one understands the associator concept of ASIO.

Still I am convinced that as a user of this library I need to know what is
going on in `await_transform` of different types, and when I am engaging
them.

I hope this makes sense. I have little experience with coroutines, so I do
not know which of the choices applied in Boost.Async are a necessary part
of every C++ coroutine library, and which are unique choices specific to
this one.

Regards,
&rzej;

>
>
> >
> > I wonder what others think about it?
> >
> > Regards,
> > &rzej;
> >
> > _______________________________________________
> > Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk