-
Notifications
You must be signed in to change notification settings - Fork 584
Fix memory leak in AWAIT_ON_READY #2268
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes a memory leak in the AWAIT_ON_READY
method by simplifying its implementation. The original code used _finally()
which created intermediate promise objects that weren't settled until event loop termination, causing memory growth over time.
- Replaces
_finally()
call with direct callback registration - Eliminates intermediate promise objects that caused memory accumulation
- Maintains the same functional behavior while improving performance
lib/Mojo/Promise.pm
Outdated
push @{$self->{resolve}}, $cb; | ||
push @{$self->{reject}}, $cb; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new implementation changes the behavior from the original _finally() approach. The original code used catch(sub { }) to swallow errors, but the new implementation will propagate errors through the callback. This could be a breaking change if callers expect errors to be silently handled.
push @{$self->{resolve}}, $cb; | |
push @{$self->{reject}}, $cb; | |
push @{$self->{resolve}}, sub { eval { $cb->(@_) } or do { }; }; | |
push @{$self->{reject}}, sub { eval { $cb->(@_) } or do { }; }; |
Copilot uses AI. Check for mistakes.
Perltidy failed. |
The original implementation used _finally(), which created multiple intermediate promise objects. These weren't settled until the event loop stopped, creating a memory leak and significant delay on stop. Simplified AWAIT_ON_READY to directly register callbacks without intermediate objects, eliminating the leak while maintaining correct functionality. This will also be quicker. Fixes mojolicious#2220
In case it needs to be said, I reject Copilot's suggestions because the AWAIT_ON_READY method is entirely specific to Future::AsyncAwait. If it works, it works. Having said that, Mojo::Promise is complex, and I'd appreciate a second opinion from someone with a better working knowledge of it. |
It does not need to be said. 😀 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that this has been confirmed to work for those affected and does not appear to have any negative side effects, i'll give it a 👍 .
That would be my logic too. So I guess I'll be the deciding factor |
Please investigate the report from #2276 or we will be forced to revert the merge. |
Will do, but I need a test case. The problem might not be in the fix. |
Summarising my analysis in #2276 my conclusion is that this fix exposes a bug in DBD::Pg: the timing of the statement handle destruction can cause concurrent handles to fail. It's possible to work around this in various ways, but there's no obvious and simple way to do it in the Mojo code. |
Summary
Change implementation of Mojo::Promise::AWAIT_ON_READY() to fix leak.
Motivation
The original implementation used _finally(), which created multiple intermediate promise objects. These weren't settled until the event loop stopped, creating a memory leak and significant delay on stop. The simplified code directly registers callbacks without intermediate Promises, eliminating the leak while maintaining correct functionality. This will also be quicker.
Memory::Usage before fix.
Memory::Usage after fix.
References
Fixes #2220