Hi there.
I have a question about Wx::Perl::ProcessStream and memory usage.
My background is a long running GUI application that (from time to time)
executes external commands, for example running rsync processes. In my GUI I'd
like to give some feedback about what is happening. My plan was to have
Wx::Perl::ProcessStream runs the rsync command and to use an eventhandler for
STDOUT to process the output of a dry run (basically a long list of filenames).
However, while processing the output, W:P:PS seems to allocate memory and
running a rsync process again does not reuse the memory. Per 10.000 lines of
STDOUT processed, my process grows by 45MB (Debian Lenny, perl 5.10, Wx v0.92,
wxWidgets 2.6.3.2, on winXP it is 33MB but basically the same problem). I have
built a test case (attached) to illustrate the behaviour. Just run a command
that produces output on STDOUT a few times in a row and watch the memory usage
of the perl process (using top or process manager).
Now my questions:
* is my usage of W:P:PS "correct" or am I misunderstanding the concept?
* is this a known problem? I found nothing on the net so far.
* is there a way force W:P:PS ro release allocated memory back to perl once it
has processed STDOUT and the external process terminated?
* do you have any other suggestions how to handle the scenario outlined above?
I've had a look at Padre's background task handling but I wanted to avoid
having to use threads ...
Any help or pointers are appreciated!
-Cornelius
#!/usr/bin/perl
use Wx qw( :allclasses );
use strict;
use warnings;
package MyFrame;
use strict;
use warnings;
use Wx qw( :everything) ;
use Wx::Event qw( EVT_BUTTON );
use Wx::Perl::ProcessStream qw(
EVT_WXP_PROCESS_STREAM_STDOUT
EVT_WXP_PROCESS_STREAM_STDERR
EVT_WXP_PROCESS_STREAM_EXIT
wxpSIGKILL
);
use base qw( Wx::Frame );
sub new {
my $self = shift;
$self = $self->SUPER::new(
undef, wxID_ANY, 'Test',
wxDefaultPosition,
Wx::Size->new(800, 600),
wxDEFAULT_FRAME_STYLE
);
$self->SetSizer(Wx::BoxSizer->new(wxVERTICAL));
# create panel, panel-sizer
$self->{panel} = Wx::Panel->new(
$self, wxID_ANY,
wxDefaultPosition, wxDefaultSize,
wxBORDER_SIMPLE
);
$self->{panel}->SetBackgroundColour(Wx::Colour->new(255, 255, 255));
$self->{panel}->SetSizer(Wx::BoxSizer->new(wxVERTICAL));
# add panel to frame-sizer
$self->GetSizer->Add(
$self->{panel}, 1,
wxALIGN_CENTER_HORIZONTAL|wxGROW|wxALL, 5
);
# create text, button and status
$self->{label} = Wx::StaticText->new(
$self->{panel}, wxID_ANY, 'command: ',
wxDefaultPosition, wxDefaultSize,
wxALIGN_CENTER_HORIZONTAL|wxGROW|wxALL, 5
);
$self->{cmd} = Wx::TextCtrl->new(
$self->{panel}, wxID_ANY, 'ls -laR /usr/lib',
wxDefaultPosition, wxDefaultSize,
);
$self->{button} = Wx::Button->new(
$self->{panel}, wxID_ANY, 'Run'
);
$self->{status} = Wx::StaticText->new(
$self->{panel}, wxID_ANY, '',
wxDefaultPosition, wxDefaultSize,
wxALIGN_CENTER_HORIZONTAL|wxGROW|wxALL, 5
);
# attach event to button
EVT_BUTTON($self, $self->{button}->GetId, \&_evt_button_clicked);
# ProcessStream events
EVT_WXP_PROCESS_STREAM_STDOUT($self, \&_evt_stdout);
EVT_WXP_PROCESS_STREAM_STDERR($self, \&_evt_stderr);
EVT_WXP_PROCESS_STREAM_EXIT($self, \&_evt_exit);
Wx::Perl::ProcessStream->SetDefaultAppCloseAction( wxpSIGKILL );
# add elements to panel-sizer
for (qw( label cmd button status )) {
$self->{panel}->GetSizer->Add(
$self->{$_},0 ,
wxALIGN_CENTER_HORIZONTAL|wxALL, 5
);
}
$self->Layout;
return $self;
}
sub _evt_button_clicked {
my ($self, $event) = @_;
$self->{button}->Disable;
my $cmd = $self->{cmd}->GetValue;
$self->{linecount} = 0;
$self->{status}->SetLabel(sprintf(
'starting command "%s" at %d', $cmd, time()
));
my $process = Wx::Perl::ProcessStream->OpenProcess(
$cmd, 'external command', $self
);
$self->{button}->SetLabel('Run again');
(defined $process) or $self->{button}->Enable;
$self->{panel}->Layout;
return;
}
sub _evt_stdout {
my ($self, $event) = @_;
$event->Skip();
my $line = $event->GetLine;
$self->{linecount}++;
$self->{status}->SetLabel(sprintf('lines: %d', $self->{linecount}));
}
sub _evt_stderr {
my ($self, $event) = @_;
$event->Skip(0);
print STDERR "Error ", $event->GetLine;
}
sub _evt_exit {
my ($self, $event) = @_;
$event->Skip(0);
my $exitcode = $event->GetProcess->GetExitCode();
my $procname = $event->GetProcess->GetProcessName();
$event->GetProcess->Destroy;
print STDERR qq(EXIT: $procname: $exitcode\n);
$self->{status}->SetLabel(
$self->{status}->GetLabel . ' (finished)'
);
$self->{button}->Enable;
$self->{panel}->Layout;
}
1;
package main;
unless(caller){
no warnings 'redefine';
local *Wx::App::OnInit = sub{1};
my $app = Wx::App->new;
Wx::InitAllImageHandlers();
my $frame = MyFrame->new;
$app->SetTopWindow($frame);
$frame->Centre;
$frame->Show(1);
$app->MainLoop;
}