Signals & MacOS
- From: Jeffrey Ratcliffe <jeffrey ratcliffe gmail com>
- To: "gtk-perl-list gnome org List" <gtk-perl-list gnome org>
- Subject: Signals & MacOS
- Date: Fri, 13 Mar 2015 22:41:14 +0100
Now, at last with a small working example. The following code works
fine on Linux, but on MacOS:
a. the gui does not seem to respond to the "hup" signal from the subprocess
b. gets far more "in" signals than it should
Unfortunately, the above description is secondhand, as I do not have
access to a Mac.
I would be very glad if somebody could give me a clue how to get
things working under both operating systems.
Regards
Jeff
#!/usr/bin/perl
use warnings;
use strict;
use Gtk2 -init;
use Glib qw(TRUE FALSE); # To get TRUE and FALSE
use POSIX qw(locale_h :signal_h :errno_h :sys_wait_h);
use IPC::Open3;
use IO::Handle;
use Readonly;
Readonly my $_POLL_INTERVAL => 100; # ms
Readonly my $_1KB => 1024;
my $EMPTY = q{};
# Create the windows
my $window = Gtk2::Window->new('toplevel');
my $box = Gtk2::VBox->new;
my $entry = Gtk2::Entry->new;
my $pbar = Gtk2::ProgressBar->new;
my $qbutton = Gtk2::Button->new('Quit');
my $sbutton = Gtk2::Button->new('Start');
$window->add($box);
$box->add($pbar);
$box->add($qbutton);
$box->add($sbutton);
# We should also link this to the destroy message on the main window,
# this is just quick and dirty
$qbutton->signal_connect( clicked => sub { Gtk2->main_quit } );
$sbutton->signal_connect( clicked => \&start_process );
$window->show_all;
Gtk2->main;
sub start_process {
watch_cmd(
cmd => 'for i in `seq 1 5`; do echo $i; sleep 1; done',
running_callback => sub {
$pbar->pulse;
},
started_callback => sub {
$pbar->set_text('Started');
},
out_callback => sub {
my ($line) = @_;
$pbar->set_text($line);
},
err_callback => sub {
my ($line) = @_;
$pbar->set_text("Error: $line");
},
finished_callback => sub {
$pbar->set_text('Finished');
},
);
return;
}
sub watch_cmd {
my (%options) = @_;
my $out_finished = FALSE;
my $err_finished = FALSE;
my $error_flag = FALSE;
print "$options{cmd}\n";
if ( defined $options{running_callback} ) {
my $timer = Glib::Timeout->add(
$_POLL_INTERVAL,
sub {
$options{running_callback}->();
return Glib::SOURCE_REMOVE
if ( $out_finished or $err_finished );
return Glib::SOURCE_CONTINUE;
}
);
}
my ( $write, $read );
my $error = IO::Handle->new;
my $pid = IPC::Open3::open3( $write, $read, $error, $options{cmd} );
print "Forked PID $pid\n";
if ( defined $options{started_callback} ) { $options{started_callback}->() }
my ( $stdout, $stderr, $error_message );
add_watch(
$read,
sub {
my ($line) = @_;
$stdout .= $line;
if ( defined $options{out_callback} ) {
$options{out_callback}->($line);
}
},
sub {
# Don't flag this until after the callback to avoid the race condition
# where stdout is truncated by stderr prematurely reaping the process
$out_finished = TRUE;
},
sub {
($error_message) = @_;
$error_flag = TRUE;
}
);
add_watch(
$error,
sub {
my ($line) = @_;
$stderr .= $line;
if ( defined $options{err_callback} ) {
$options{err_callback}->($line);
}
},
sub {
# Don't flag this until after the callback to avoid the race condition
# where stderr is truncated by stdout prematurely reaping the process
$err_finished = TRUE;
},
sub {
($error_message) = @_;
$error_flag = TRUE;
}
);
# Watch for the process to hang up before running the finished callback
Glib::Child->watch_add(
$pid,
sub {
# Although the process has hung up, we may still have output to read,
# so wait until the _watch_add flags that the process has ended first.
my $timer = Glib::Timeout->add(
$_POLL_INTERVAL,
sub {
if ($error_flag) {
if ( defined $options{error_callback} ) {
$options{error_callback}->($error_message);
}
return Glib::SOURCE_REMOVE;
}
elsif ( $out_finished and $err_finished ) {
if ( defined $options{finished_callback} ) {
$options{finished_callback}->( $stdout, $stderr );
}
print "Waiting to reap process\n";
# -1 indicates a non-blocking wait for all pending zombie processes
print 'Reaped PID ', waitpid(
-1, ## no critic (ProhibitMagicNumbers)
WNOHANG
),
"\n";
return Glib::SOURCE_REMOVE;
}
return Glib::SOURCE_CONTINUE;
}
);
}
);
return;
}
sub add_watch {
my ( $fh, $line_callback, $finished_callback, $error_callback ) = @_;
my $line;
Glib::IO->add_watch(
fileno($fh),
[ 'in', 'hup' ],
sub {
my ( $fileno, $condition ) = @_;
my $buffer;
if ( $condition & 'in' ) { # bit field operation. >= would also work
# For Linux, this "if" should always return true, as the
# callback is only triggered when there is data to read.
# MacOS seems to trigger this callback even when there is
# nothing to read, and therefore we need this conditional
# Only reading one buffer, rather than until sysread gives EOF
# because things seem to be strange for stderr
if ( sysread $fh, $buffer, $_1KB ) {
if ($buffer) { $line .= $buffer }
while ( $line =~ /([\r\n])/xsm ) {
my $le = $1;
if ( defined $line_callback ) {
$line_callback->(
substr $line, 0, index( $line, $le ) + 1
);
}
$line = substr $line, index( $line, $le ) + 1,
length $line;
}
}
}
# Only allow the hup if sure an empty buffer has been read.
if (
( $condition & 'hup' ) # bit field operation. >= would also work
and ( not defined $buffer or $buffer eq $EMPTY )
)
{
if ( close $fh ) {
$finished_callback->();
}
elsif ( defined $error_callback ) {
$error_callback->('Error closing filehandle');
}
return Glib::SOURCE_REMOVE;
}
return Glib::SOURCE_CONTINUE;
}
);
return;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]