Re: GUI-safe polling loop
- From: muppet <scott asofyet org>
- To: Matthew Braid <ptkperl mdb id au>
- Cc: gtk-perl-list gnome org
- Subject: Re: GUI-safe polling loop
- Date: Thu, 1 Feb 2007 22:05:34 -0500
On Feb 1, 2007, at 8:10 PM, Matthew Braid wrote:
Just wanted to know if what I'm doing makes sense.
I'm trying to poll for a child process being finished in a way that
will not freeze the GUI up, but also won't let the surrounding code
continue until the child process is finished.
GLib provides GChildWatch, which is a GSource that fires when a given
pid exits. The Right Way to do this is along the lines of
my $loop = Glib::MainLoop->new;
$id = Glib::ChildWatch->add ($child_pid, sub { $loop->exit; 0 });
$loop->run;
However, on looking through GMainLoop.xs just now, it appears that
g_child_watch_add() is not bound to perl.
:-P
Another thought would be to create your own GSource in perl, and hook
that into the main loop. Of course, the utilities you need to do
that are (you guessed it) also not bound to perl.
Dammit.
Assuming $kid is the process id of the child, is the following code
reasonable or am I missing something basic:
use POSIX qw/WNOHANG/;
require Glib;
#...
while (1) {
last if waitpid($kid, WNOHANG); # Check if we're done
my $ml = Glib::MainLoop->new;
Glib::Timeout->add(2000, sub { $ml->quit; return 0 });
$ml->run;
}
This gives you a timer granularity, which means that you won't
acknowledge the child's termination in a timely manner. To fix that,
you shorten the timeout, which increases the load.
The real solution is to use something that fires from SIGCHLD to wake
up a source being polled by the main loop. (This is what GChildWatch
does...) So, a pure-perl reimplementation of GChildWatch might look
like this:
#!/usr/bin/perl -w
package Mup::ChildWatch;
use strict;
use Glib;
#
# A little private mapping of PIDs to callbacks and their data.
#
my %callback;
#
# One of the very few things that are safe to do from async signal
handlers
# is to write to a pipe. It just so happens that pipes are one of
the things
# we can easily monitor in the main loop. So, here, we'll create a
pipe to
# which we'll write in the SIGCHLD handler, and then monitor in the
main loop.
#
pipe CHLDEXITREAD, CHLDEXITWRITE;
#
# Install our CHLD handler. Of course, it's usually very bad form
for library
# code to install signal handlers, so i don't think of this as a perfect
# solution... but in here, when we get a CHLD, we'll tickle the pipe
with a
# byte so as to wake up the event source.
#
$SIG{CHLD} = sub { syswrite CHLDEXITWRITE, "1"; };
#
# Now, install a watch on the other end of the pipe. This will wake
up in the
# main loop when the signal handler has written to it. That's our
signal to
# reap all the processes we can, invoking the appropriate callbacks
if any
# are registered.
#
Glib::IO->add_watch (fileno CHLDEXITREAD, 'in', sub {
my $dummy;
sysread CHLDEXITREAD, $dummy, 1;
while (-1 != (my $pid = wait)) {
print "reaped pid $pid\n";
use Data::Dumper;
print Dumper(\%callback);
if ($callback{$pid}) {
print "invoking callback ... \n";
$callback{$pid}{callback}->($callback{$pid}{data});
__PACKAGE__->remove ($pid);
}
}
1;
});
#
# And some public interface that walks and talks like
Glib::ChildWatch would.
#
sub add {
my ($class, $pid, $callback, $data) = @_;
$callback{$pid} = { callback => $callback, data => $data };
return $pid;
}
sub remove {
my ($class, $pid) = @_;
delete $callback{$pid};
}
package main;
my $pid = fork;
die "couldn't fork: $!\n" unless defined $pid;
if (!$pid) {
sleep 1 + rand(2);
exit 0;
}
my $loop = Glib::MainLoop->new;
Mup::ChildWatch->add ($pid, sub { print "whee\n"; $loop->quit });
$loop->run;
__END__
--
Doing a good job around here is like wetting your pants in a dark
suit; you get a warm feeling, but no one notices.
-- unknown
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]