grinder has asked for the wisdom of the Perl Monks concerning the following question:

Monk, I am in a maze of twisty interprocess communications, all different.

I have a process that spawns a number of kids. It reads a line from a data file, and then writes that line to each kid. A kid may choose to reply, or it may remain silent. I've been looking for the best way to handle this. I can assume that if the kid doesn't reply within one second, it's as if it never will.

I've been perusing the Cookboot, perlipc and IPC::Open2 and I can't get no satisfaction (woah-ho-ho). I hesitate to show the code I've been toying with, because it will show what an amateur I am at this. I have done quite a bit of this sort of stuff, but I've always been able to get away with the fact that the problem at hand mapped nicely to one of the examples. I think the fact that a child may remain silent is the trickiest thing (as in: non-blocking reads).

It seemed to me that the nicest solution was to use socketpair, as per perlipc. But after I started to bend the example more into the shape I wanted it to be, I think I ran into a wall of [bs]uffering.

So here's the code:

#! /usr/bin/perl use strict; use constant DUMMY => 1; use Socket; use IO::Handle; socketpair(CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "soc +ketpair client: $!"; CHILD->autoflush(1); PARENT->autoflush(1); if (my $kid = fork) { close PARENT; for my $key( @ARGV ) { print "parent($$) send [$key]\n"; print CHILD "$key\n"; chomp(my $line = <CHILD>); print "parent($$) recv [$line]\n"; } close CHILD; waitpid($kid,0); } else { die "cannot fork: $!" unless defined $kid; close CHILD; if( DUMMY ) { while( chomp(my $line = <PARENT>) ) { print "child($$) recv [$line]\n"; select undef, undef, undef, 0.2; if( rand() < 0.5 ) { my $now = "$line " . time; print "child($$) send [$now]\n"; print PARENT "$now\n"; } } close PARENT; } else { exec qw{ /bin/foo -c bar } or die "child could not exec: $!\n" +; } }

The above works if DUMMY is set to 1 and the kid replies each time. And if I set DUMMY to 0, in order to make the kid exec the program I really want to use it dies with a sullen Broken pipe, and I don't understand why. I know I need a select loop to see if there are handles with data waiting on them, but when I read the man page I feel like a rabbit caught in headlights.

What's more, the code above is for one kid, I need an array of them.

My dream is that I would be able to hide all this nasty stuff behind subroutines, which would in turn let me Memoize them, because it turns out that I ask the kids the same question over and over again (a bit like real life, eh?)

Thanks for any clues I can use.

update: bbfu's remark taken: this is not for Win32, so I'm not worried about Windowisms

Replies are listed 'Best First'.
Re: Non-blocking IPC to and from multiple children
by bbfu (Curate) on Mar 29, 2004 at 15:33 UTC

    I would use pipes instead of socketpairs, but mostly because I'm not really familiar with socketpairs.

    The following code doesn't work on Win32, since select is (properly) implemented only for sockets on Win32. Presumably, you could use the same basic idea but with socketpairs and have it work on Win32 as well. *shrug*

    #!/usr/bin/perl use warnings; use strict; use IO::Handle; use IO::Pipe; use IO::Select; my $stdout = IO::Pipe->new(); my $stdin = IO::Pipe->new(); defined(my $pid = fork) or die "Fork failed\n"; if($pid) { $stdout->reader(); $stdin ->writer(); $stdin ->autoflush(1); my $select = IO::Select->new($stdout); foreach my $key (@ARGV) { $stdin->print("parent ($$) send [$key]\n"); next unless $select->can_read(1); $stdout->sysread(my $buf, 1024); chomp($buf); print "parent ($$) recv [$buf]\n"; } $stdin->close(); waitpid($pid, 0); } else { $stdout->writer(); $stdin ->reader(); $stdout->autoflush(1); open STDOUT, ">&", $stdout; open STDIN, "<&", $stdin; exec "$^X", "tst.pl"; }

    And tst.pl:

    #!/usr/bin/perl use warnings; use strict; $|++; while(<STDIN>) { chomp; next unless /\[(\d+)\]/ && $1 % 2 == 1; # Only odds print "$1 is odd\n"; }

    bbfu
    Black flowers blossom
    Fearless on my breath

Re: Non-blocking IPC to and from multiple children
by Fletch (Bishop) on Mar 29, 2004 at 16:06 UTC

    Use POE! It'd be really easy to setup your children with POE::Wheel::Run and then just let POE handle letting you know when there's results available. Your controlling session could cache the returned results and check that before delegating a job out to a child wheel.

Re: Non-blocking IPC to and from multiple children
by zentara (Cardinal) on Mar 29, 2004 at 16:54 UTC