-
-
Notifications
You must be signed in to change notification settings - Fork 67
Memory Leak #255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Hi Daniel, I'm working on the reply to your email, but I figure I might explain some stuff here first, since I can then refer to them from the email :-) Regarding your patch - it would work in this very particular case, but in general case, what it really does is that it just pushes the memory leak one level up in the callstack. Consider the following modification of your example: #include "qcorotask.h"
#include "qcorotimer.h"
#include <QCoreApplication>
#include <QDateTime>
#include <QTimer>
#include <chrono>
#include <iostream>
using namespace std::chrono_literals;
class Bar
{
public:
Bar() {
std::cout << "Bar" << std::endl;
}
~Bar() {
std::cout << "~Bar" << std::endl;
}
};
class Baz
{
public:
Baz() { std::cout << "Baz" << std::endl; }
~Baz() { std::cout << "~Baz" << std::endl; }
};
class Dialog
{
public:
Dialog() {
std::cout << "Dialog" << std::endl;
timer.setInterval(2s);
timer.start();
QObject::connect(&timer, &QObject::destroyed, []{
std::cout << "~timer" << std::endl;
});
}
~Dialog() {
std::cout << "~Dialog" << std::endl;
}
QCoro::Task<> doSomething() {
std::cout << "doSomething started" << std::endl;
auto bar = std::make_unique<Bar>();
co_await timer;
std::cout << "doSomething ticked!" << std::endl;
qApp->quit();
}
QCoro::Task<> doSomethingOutter() {
std::cout << "doSomethingOutter started" << std::endl;
auto baz = std::make_unique<Baz>();
co_await doSomething();
std::cout << "doSomethingOutter finished" << std::endl;
}
QTimer timer;
};
QCoro::Task<> runMainTimer3(Dialog *d) {
std::cout << "runMainTimer3 started" << std::endl;
co_await d->doSomethingOutter();
std::cout << "runMainTimer3 finished" << std::endl;
}
void runMainTimer() {
auto d = new Dialog;
std::cout << "runMainTimer started" << std::endl;
runMainTimer3(d);
// This will make the bar leak
delete d;
std::cout << "runMainTimer finished" << std::endl;
}
int main(int argc, char **argv) {
QCoreApplication app{argc, argv};
QTimer ticker;
QObject::connect(&ticker, &QTimer::timeout, &app, []() {
std::cout << QDateTime::currentDateTime().toString(Qt::ISODateWithMs).toStdString()
<< " Secondary timer tick!" << std::endl;
});
ticker.start(200ms);
QTimer::singleShot(0, runMainTimer);
return app.exec();
} With your patch, when the QTimer is destroyed, you also destroy the You would need to chain up the The missing handling for cancelation/internal failures in QCoro is a repeating problem that is blocking many useful features. I neglected this case in the early stages of QCoro development, since I was building it "on the go" as I was learning coroutines. There's now a bunch of tickets about QCoro2 API, where I want to take all the lessons learned so far and requirements that I've collected over the time into account and do actual proper design. The major change compared to PS: sorry for all the edits and typos, I'm on a phone... |
Is it possible to chain up and destroy all coroutines? Eventually something is not coawaiting, runMainTimer calls runMainTimer3 for example, if it returned a bool it would not depend on runMainTimer3 result. int main() can't be a coroutine so destroying() till event loop should be fine. About my patch I think it's better with it than without, and I think you should also put a disclaimer of the limitations and current drawbacks of it. 99% of my code would not chain coroutines, but certainly will destroy the caller like in my example. I can't use QCoro as is right now, but would be fine if I knew this limitation or there was some warning/build error agains it. |
As long as the entire stack are QCoro::Tasks, then we are able to traverse that internally (but even with QCoro itself, that might not be the case as we use awaitable wrappers internally in a some places that would need to be made aware of the "chainup"). Alternatively it could be tracked externally....
Again, in this specific scenario, it would work. However, consider this scenario, where you are "spawning" a coroutine from a regular function and waiting for its result synchronously ( void myFunc() {
// ..
const auto result = QCoro::waitForFinished([]() -> QCoro::Task<std::unique_ptr<Result>> {
// ...
});
} In this case, you always need to provide some result to the caller and, what's worse, the result type is not default-constructible, so you can't even fabricate one. QCoro2 to the rescue! :)
I agree with the second part (the docs need a big overhaul for a long time), not so much with the first part. While it would solve the problem for your particular use-case, that's not the case for everyone and this would make the behavior quite strange IMO (it's already hard to debug coroutines with the current tooling as it is).
Hopefully we can fix that with the QCoro2 API - so far from what I've seen here and i the email, it all boils down to handling cancellation properly and being able to express that to the caller. Then we can hook into events like object we are awaiting on being destroyed.
I'd love to be able to detect those issues at compile time, alas documentation is all I can do right now :) |
I would argue that this wouldn't make the behavior different than what it is today, as the chained awaitable will still leak, and I think the wait for finished will likely be stuck there too and leak as well, so it's leaking less it seems :D waitForFinished is the root of all issues :P had that for a while in Cutelyst just to regret later, and explicit state on the docs that one should never use that, like image how easy it is to stack overflow if you get 500 network clients all creating QEventLoops... Is there something I could do to help with QCoro2? couldn't find a branch named like that btw.. |
QCoro2 is still in what I like to think about as a design phase: https://github.com/qcoro/qcoro/issues?q=is%3Aissue+is%3Aopen+label%3Aqcoro2 Your inputs, especially about underlying QObjects going away, are definitely something I'll be considering when I revisit the documents. Of course feel free to comment under the issues if you feel something is wrong or missing - it's definitely not complete yet and I'll appreciate any early feedback, otherwise it's just me talking to myself in Github issues :D |
Hi,
I've sent you a long email about some questions that I have and one of them I was able to reproduce and also fix it, but before pushing a patch I'd like to see if yo agree with my change:
You can test this and this code will make the
bar
variable leak, and this is a valid Qt code, say you click save() button, and then close the Window.I was able to solve this at qcorotimer.cpp::await_suspend() adding:
Since QCoroTimer is not a subclass this means all specialization of Qt classes should have this, not a big deal, just a bit of code duplication. Should I go and add these?
QCoroSignal which IMHO could be the base of these classes also lack the hability to destroy the handler.
The text was updated successfully, but these errors were encountered: