Boost logo

Boost :

From: Vinícius dos Santos Oliveira (vini.ipsmaker_at_[hidden])
Date: 2023-10-03 03:32:59


Em seg., 2 de out. de 2023 às 13:23, Klemens Morgenstern <
klemensdavidmorgenstern_at_[hidden]> escreveu:

> > Can you explain to me what an asynchronous coroutine is? As far as I
> see, the context-switch always happens in well defined points/cooperatively
> (thus synchronously).
>
> asynchronous in my lingo means that the coroutine might suspend to an
> asynchronous event source, e.g. a timer expiring.
> This is my lingo, but is how other languages that provide an async
> keyword do it, too.
>

I'm well aware of languages that perform free-style terming. And everybody
mirrors NodeJS these days...

However the term there is short for “async IO” rather than async
coroutines. In fact, coroutines don't even need to be marked as “async”.
Chris Kohlhoff tried to persuade the C++ committee to adopt a keyword-less
approach back in 2015:
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0114r0.pdf

Lua coroutines don't need such keywords either and are in fact more
powerful (they can suspend the caller/parent) which makes it possible for
them to implement fibers. Lua isn't the only language that exhibits this
property. Same goes for Golang's goroutines.

Async means a lack of synchronization. You can combo the term with others
to give it more meaning (e.g. async IO).

So if I were to call std::coroutine_handle<some_async_coro>::resume()
> I can't assume it's done (reached a yield or a return) when resume
> returns.
> This is different from a synchronous coroutine like std::generator,
> where the .resume() will only occur after a value has been yielded or
> the generator returned.
>

I like this example.

>> The difference between a fibers facility and just coroutines is that
> with fibers, you have a scheduler as well.
>
> Well, then what I call an async coroutine, is a conceptual fiber
> implemented via coroutines.
> I say conceptual, because boost.fiber and the paper you cite below use
> an entirely different API from what my coroutines do.
>

That's correct. You followed the reasoning.

> We have some of the best experts in this domain in our own community, and
> a few of them spared the time just on this topic as well:
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4024.pdf
>
> I would like to point out that the paper does compare the author's
> coroutine paper to their fiber paper. It does not compare fibers
> (since it was written in 2014) to the coroutines standardized in
> C++20, and thus also provide no await mechanism.
>

Both concepts predate their Boost libraries counterpart (and C++20's as
well). C++20 didn't invent coroutines.

It's easy to distinguish these concepts anyway. Fibers are meant as
"lightweight threads" and as such any fiber library (Boost or not) will
need to answer "what fiber should I *schedule* once current one can't make
further progress?".

Coroutines are a generalization of subroutines that add two new operations:
suspend and resume. You can dig into its history easily nowadays.

> Communities such as Ruby do misnomer coroutines and fibers, but we
> shouldn't be learning from incorrect material. Just as much, there are
> plenty of communities for which coroutines are just a syntax sugar for
> promises, but then again: we shouldn't be learning from incorrect material.
> Coroutines are not syntax sugars for promise objects. Coroutines are a
> broader topic. The most popular implementation nowadays are promises
> objects, but beyond a function that resumes, all the other details that
> involve these promise objects have nothing to do with coroutines.
>
> I am not familiar with ruby, but I reckon you'd consider goroutines to
> be fibers?
>

Not quite. If we want to be technically correct, goroutines are neither.
Fibers come from threading terminology which is a concurrency model that
competes against CSP (don't communicate by sharing memory; share memory by
communicating). However fibers are closer to goroutines than coroutines.
Both fibers and goroutines implicitly require a scheduler. Both represent
the "unit of concurrency" in their own concurrency model.

Ruby is a weird "impure duck". I do like these pragmatic solutions (my own
solution based on Lua and Boost.Asio mixes actors from the actor model and
fibers). My dislike for Ruby is just in their minor misnomer here.

The promise issue is why my library makes a distinction between an
> awaitable and a coroutine. It's not just syntactic sugar for promises,
> although I wonder why that would be a bad thing? What's wrong with
> using coroutines to get out of callback-hell?
>

> And I do agree with you, that coroutines are a wider topic, which is
> why my library is called async. I don't think it's the one true
> coroutine library, nor is it meant to be.
>
> Rather the idea is to allow it to interact with other coroutine
> libraries. For example, if you were to build a coroutine based stream
> parser you should be able to make it work asynchronously with an io
> stream based on boost.async.
>

Fair enough.

> So, again, could you teach me what an asynchronous coroutine is? This is
> really a topic that I spend a lot of time mastering. Any new thing that I
> might learn here would be appreciated.
>
> The coroutines you get with the async keyword in python or javascript,
> which means coroutines that run on a scheduler.
> Both languages have coroutines that are not async, too.
>

They happen asynchronously to what exactly? If there are asynchronous
coroutines, there are synchronous coroutines. What distinguishes them? I
never saw the term "asynchronous coroutines" anywhere before.

I just see a lack of rigorous analysis here. To be honest, if you just
mirror the design of NodeJS or whatever, I can't acknowledge you as an
expert in this domain.

What these languages really provide:

   - An ecosystem of multiple independent concepts working together with
   proper synergy.
   - Among these concepts, a scheduler that concurrently (and
   synchronously, not asynchronously) switches between tasks.
   - Among these concepts, also an object to represent a task (a promise).
   - Also among these concepts, a coroutine at the language-level to remove
   the boilerplate of writing objects implementing the interface for a promise.
   - As an optimization when the host operating system provides (e.g.
   Windows IOCP, Linux io_uring) truly asynchronous IO support (a bigger topic
   that Boost.Asio already gets right).

I did learn from NodeJS and it was good at the time as it helped to push
people for something better. However I also learnt from Boost.Asio,
Boost.Coroutine, Boost.Context, Boost.Asio, and plenty of members inside
and outside Boost's community. Honestly, NodeJS just got stuck in time. It
can't hope to grow more than it already did. If you provide a complete copy
of NodeJS inside C++ so NodeJS programmers can come over, what is it that
you'll offer for them to come here? They already have NodeJS. You need a
differentiating factor.

-- 
Vinícius dos Santos Oliveira
https://vinipsmaker.github.io/

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