danvratil/qcoro

Awaiting with context object.

Opened this issue · 2 comments

In some cases, one might want to update an object (for example a model), when a coroutine finished.
This can be done by either using a lambda that captures this as a coroutine, or by adding a coroutine member function.
However there is no guarantee that once the coroutine finishes, the object still exists, so this can lead to crashes.

It is pretty much the same issue as the 3-arg connect in Qt: https://github.com/KDE/clazy/blob/master/docs/checks/README-connect-3arg-lambda.md

Is this already a solved problem in some way that I have missed?

If not, I propose adding an overload of QCoro::Task<T>::then that takes a QObject pointer as an additional argument, so that it can connect to QObject::destroyed of the context object, and not invoke the continuation if the signal has been emitted in the meantime.

I currently can't find a good way to fix this when using the co_await keyword, but as this mostly concerns the issue of interaction between coroutine code and sync code, support in .then is probably enough.

If needed, there could be something like

co_await someFunctionReturningTask().withContext(this)

to inject the context even though using co_await.

I already have code for some of the proposed things here, so I can work on implementing it if needed.

Hi Jonah,

adding the context to .then() makes sense, but I think it's not as simple as it sounds:

QCoro::Task<int> task = gimmeTask();
const auto result = co_await task.then(someObject, [someObject](int result) {
    return someObject->magicalNumber() + result;
});

If someObject is destroyed while the task is still running, you will skip calling the .then() continuation, but now you need to figure out what to do with the co_await, because you don't have a result for it. Should it simply not be resumed, ever? This might make the application stuck...

One option would be to join this with the #107 and allow context object only on CancellableTask<T>::then(), where the result of the chain would be a cancelled Result<U>, if the context object was destroyed.

That seems like a good idea!