qcoro: A slot called by qt, which is a coroutine: handling exceptions
Hi, I have this kind of code:
#include <iostream>
#include <QTimer>
#include <QCoreApplication>
#include <QCoroSignal>
struct Response {
};
class ServerConnection : public QObject {
Q_OBJECT
public:
ServerConnection()
{
// connect to the server...
emit serverConnected();
}
Q_SIGNAL void serverConnected();
};
QCoro::Task<Response> sendMessage();
QCoro::Task<> onServerConnected()
{
std::cerr << "doStuff()\n";
// ... do stuff, maybe use sendMessage with co_await
//
// ...
//
throw std::runtime_error("Can't catch this");
}
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
auto srv_conn = new ServerConnection();
QObject::connect(srv_conn, &ServerConnection::serverConnected, onServerConnected);
app.exec();
}
I want to rework my current app to use qcoro. However, I don’t want to rewrite all of the stuff at once. In the above code, I’d like to use coroutines inside the doStuff function. This means that doStuff itself must be a couroutine. My issue is that if doStuff throws, I have no way to handle that exception, because Qt doesn’t call it with co_await (I’m not even sure how that would work!).
What do you think is the correct approach? Should I just wrap my “top-level” couroutine with a try-catch block and handle the exceptions there?
About this issue
- Original URL
- State: open
- Created 2 years ago
- Comments: 20 (10 by maintainers)
Coincidentally, this is possible with QCoro. We have to have a special trick where the coroutine is aware of the
Task’s lifetime. Normally the lifetime of a coroutine is bound to the lifetime of theTask, so whenTaskis destroyed, the coroutine is destroyed. But this only works when the coroutine is alwaysco_awaited, which is not the case with a coroutine being called from an event loop - the event loop will discard theTaskreturned from the coroutine, which would normally destroy the coroutine before it would start. So what we do is that ifTaskis destroyed while the coroutine is still running, it will not destroy the coroutine, but instead the coroutine will self-destruct when it reaches the final suspend point.To put it shortly, yes, we are able to detect when coroutine has finished without being
co_awaited, so we could rethrow the exception at that point, if it holds any.I like your idea of an additional template parameter to
Taskto enable this behavior. I’ll try to put something together and send you a link to PR so you can try it out.This is the discussion thread for JS: https://github.com/nodejs/node/issues/20392 It seems that it’s a matter of opinion rather than some kind of a technical issue. It seems that the creators of Node.js eventually agreed that unhandled rejections should just terminate the program.
If similar behavior were to be implemented in QCoro, the default would be to terminate on unhandled exceptions.
co_awaitcan still rethrow them as normal.IMHO, non-awaited exceptions should just crash the program. Ignoring them could lead to undefined behavior and/or memory leaks.