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

Reply via email to