openSUSE / openSUSE/Mojo-IOLoop-ReadWriteProcess
Leaks and eventually runs out of file descriptors due to a ref cycle
Nobody has claimed this yet.
- Dominant language
- Perl
- Stars
- 12
- Forks
- 14
- PR merge metrics
- No merged PRs in 30d
Description
Hi,
the docs don't mention this so I don't think I am "holding it wrong". If I do this:
sub do_a_thing
{
my $self = shift;
my $process = process(execute => '/usr/local/bin/whatever')->args(['arg', 'arg']);
$self->child_process($process);
$process->on(stop => sub {
my $process = shift;
if ($process->exit_status)
{
warn "child failed: " . $process->exit_status;
$self->clear_child_process;
return;
}
my $output = $process->read_stdout;
warn $output;
$self->clear_child_process;
return;
});
$process->start;
}
After the process exits, I don't hold any handles to $process, so I'd expect it to go out of scope and eventually lose the file descriptors for the IPC pipes. But it doesn't, because of a cycle:
Cycle (1):
$Mojo::IOLoop::ReadWriteProcess::A->{'session'} => \%Mojo::IOLoop::ReadWriteProcess::Session::B
$Mojo::IOLoop::ReadWriteProcess::Session::B->{'process_table'} => \%C
$C->{'2160'} => \$D
$$D => \%Mojo::IOLoop::ReadWriteProcess::A
and this never gets called:
package Mojo::IOLoop::ReadWriteProcess;
sub DESTROY
{
warn "destroying $_[0]";
}
I can resolve this manually by calling $process->session->clean, but I think the "proper" fix is for this ref to be weakened immediately after it is created:
I've verified the following monkey patch works which means "weaken($process)" above would be a bare minimum fix:
package Mojo::IOLoop::ReadWriteProcess::Session;
use Scalar::Util qw(weaken);
sub register
{
my $self = shift;
my $pid = shift;
my $process = shift;
$self->process_table()->{$pid} = \$process;
weaken($process);
$self->emit(register => $process);
}
But there's probably a wider architectural question, since process "registers" itself to the session when it starts, perhaps it should also "unregister" itself from the session when it stops so it's not just relying on going out of scope to drop the (by then defunct) pipes.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in lib/Mojo/IOLoop/ReadWriteProcess/Session.pm at the register implementation around lines 101-105, then trace how the process table is cleaned when a process stops. Reproduce the example and inspect the reference cycle and DESTROY behavior. Done means stopped processes no longer remain retained by the session or leak their IPC file descriptors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- perl
- Domain
- operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100