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; }