danvratil/qcoro

Documentation needs clarity about handling coroutine return type

Opened this issue · 2 comments

Neither README.md, code examples (that are not even standalone applications), nor "C++ Coroutines and Qt" KDE Akademy slides shows what do you do with QCoro::Task<> instance returned by a coroutine.

I did found paragraph in documentation "Interfacing with synchronous functions", stating:

Sometimes you need to interface with code that is not coroutine-aware, for example when building list models.

Is it really "sometimes"?

I mean, Qt does not have built-in support for coroutines, so in many (most?) cases, if you would have slot like:

void on_someButton_clicked()
{
 /// ???
}

You'll have to use QCoro::connect()?

I mean, if Qts signal-slot system would "natively" support coroutines, maybe this would work:

QCoro::Task<void> on_someButton_clicked()
{
// multiple co_awaits
}

But it does not..?

So in the end we have to use QCoro::connect() basically everywhere (not "sometimes"), and result is kinda similar (i.e. verbose, hard to read) if I would be using QObject::connect with lambdas..? Or am I missing something?

Also, if coroutine is invoked in non-coroutine on_someButton_clicked() (and there HAS to be at lest one non-coroutine, main() as minimum) as shown above, what do I do with QCoro::Task<> instance? Do I have to keep it, std::move()'it around, store it as class member variable? Or is it just basically shared pointer, and the using QCoro::connect makes a shallow copy and it keeps that way alive? These questions arises when looking around this project.

Also, I've missed statement bout coroutine type QCoro::Task<> being lazy or eager.

Sorry for sort of brain-dump post, but since coroutines are kinda brain-f*** that needs some time to grasp the concept of, some more detailed documentation would help, IMO.

It would be nice to have one single real working application example, showing "best practices" on how do we link Qt Widget application with QCoro-style coroutines. Maybe downloading some data from web using multiple QNetworkAccessManager requests, and then doing some calculations in QThread, etc, with everything coded in a single synchronoise-like function, which is actually a "magic" coroutine, invoked from button click event (slot).

Thanks for the feedback on the docs, you are right that it should provide more details on the fundamentals of coroutines in QCoro. I'll try to clarify at least some of your questions below:

You can check the examples directory for some examples of interfacing coroutines with Qt. This one actually uses QNetworkAccessManager with widgets. The QIODevice example is also good.

Qt discards slot return types (and values), so you can just use QObject::connect() to connect a signal to a coroutine slot. When Qt invokes the coroutine slot it just discards the returned QCoro::Task<> (it does not co_await it because as you said Qt is not coroutine-aware, which is the reason why QCoro coroutines must be eager). The coroutine will run until its first suspension point, then execution returns to Qt and back to the handler that invoked the slot and eventually to the event loop.

Destroying QCoro::Task<> does not destroy a running coroutine. The coroutine will "self-destruct" at the end. Coroutines in QCoro have a refcount of two: one reference is held by the promise and is decremented when the coroutine finishes and one is held by the associated QCoro::Task<>. As long as QCoro::Task<> is around, the coroutine is not destroyed (so result can be extracted). If it's destroyed and the coroutine is still running, the coroutine continues running until completion and then it destroys itself (the "coroutine-as-a-slot case"). If the coroutine has already finished when the associated QCoro::Task<> has been destroyed, it is destroyed as well.

I'll try to take this and turn it into proper docs.

Let me know if you have any more questions, I'd be happy to answer them (and clarify the docs accordingly).

OK so its double-reference-counted, AND eager, so in the result it works rather transparent in Qt signal-slots and event loop.

Thanks @danvratil , your explanation helped a lot!

This one actually uses QNetworkAccessManager with widgets

Ouch, I've missed that, sorry!