gepapa has asked for the wisdom of the Perl Monks concerning the following question:
So I have what I believe is a different question than the norm.
I have a server which has a distinct listener running on multiple ports. What I want to do is be able to make calls to these distinct listeners whenever I want, including simultaneously. So I don't want to necessarily wait on each pid, because I want to be able to do other stuff including making connections to other open listeners. Each call will make the server do something, and I need to get the output back from whatever it is doing.
Here is some sample code I put together to give you a basic idea of what I need to do:
use strict;
use Carp;
use ClarRPC; #In house module
my ($pid1, @resp1) = rpc(10.15.51.208, '1300', 'ping -n 15 10.15.51.20
+8');
my ($pid2, @resp2) = rpc(10.15.51.208, '1301', 'ping -n 15 10.15.51.20
+8');
my ($pid3, @resp3) = rpc(10.15.51.208, '1302', 'ping -n 15 10.15.51.20
+8');
print ("RESP1: @resp1\n");
print ("RESP2: @resp2\n");
print ("RESP3: @resp3\n");
sub rpc
{
my ($ip, $port, $command) = @_;
my @resp;
my $pid;
my @pids;
if ($pid = fork()) {
push(@pids, $pid);
#waitpid($pid, 0);
}
elsif (defined $pid) {
my $connection = ClarRPC->connect($ip, $port);
($pid, @resp) = $connection->rpc('ClarRPCService::system_call'
+, $command);
$connection->disconnect();
exit;
}
return $pid, @resp;
}
What I need is the information from @resp.
I don't care if it is out of order, and I know the print statements I have won't work.
I know I need to wait for a particular pid to complete before having data, but what I need is to be able to wait for that pid without blocking the possibility of the other pid's completing before it. So essentially I need the ability to just get the info from whatever call is complete, at any time.
Any help would be greatly appreciated
Thanks
Re: Forking Clients
by BrowserUk (Patriarch) on Oct 15, 2008 at 16:19 UTC
|
If you don't have an irrational fear of threads, something like this complicated beast would do it :):
#! perl -slw
use strict;
use threads;
use threads::shared;
use Thread::Queue;
use ClarRPC; #In house module
my $Q = new Thread::Queue;
my $ip = '10.15.51.208';
my $command = 'ping -n 15 10.15.51.208';
my @ports = 1300 .. 1302;
for my $port ( @ports ) {
async{
my $connection = ClarRPC->connect( $ip, $port );
my @results :shared = $connection->rpc(
'ClarRPCService::system_call', $command
);
$connection->disconnect();
unshift @results, $port;
$Q->enqueue( \@results, undef );
}->detach;
}
for ( 1 .. @ports ) {
while( my $ref = $Q->dequeue ) {
my( $port, @results ) = @$ref;
print "$port : @results";
}
}
__END__
c:\test>junk4
1300 : 1 2 3 4 5 6 7 8 9 10
1301 : 1 2 3 4 5 6 7 8 9 10
1302 : 1 2 3 4 5 6 7 8 9 10
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] |
|
use strict;
use threads;
use Thread::Queue;
use ClarRPC;
my $queue = new Thread::Queue;
rpc('10.15.51.208', '1300', 'ping -n 50 10.15.51.208');
rpc('10.15.51.208', '1301', 'ping -n 10 10.15.51.208');
rpc('10.15.51.208', '1302', 'ping -n 50 10.15.51.208');
while (my $ref = $queue->dequeue) {
my ($port, @results) = @$ref;
print "$port : @results\n";
}
sub rpc
{
my ($ip, $port, $command) = @_;
async{
my $connection = ClarRPC->connect($ip, $port);
my @resp :shared = $connection->rpc('ClarRPCService::system_ca
+ll', $command);
$connection->disconnect();
unshift(@resp, $port);
$queue->enqueue(\@resp, undef);
}->detach
}
So in this case note the 1301 port command is a -n 10, so this one will return prior to the others.
So what I see in this instance is that the program exits, as soon as the 1301 port returns its info. What I was hoping for was for something that wouldn't exit until they were all done. I don't see anything glaring that I missed in my version which would make it different than yours in functionality.
Any ideas? Thanks
| [reply] [d/l] |
|
use strict;
use threads;
use threads::shared; ### I omitted this from my example above initiall
+y.
use Thread::Queue;
use ClarRPC;
my $queue = new Thread::Queue;
rpc('10.15.51.208', '1300', 'ping -n 50 10.15.51.208');
rpc('10.15.51.208', '1301', 'ping -n 10 10.15.51.208');
rpc('10.15.51.208', '1302', 'ping -n 50 10.15.51.208');
for( 1 .. 3 ) { ## Must iterate once for each thread started ###
while (my $ref = $queue->dequeue) {
my ($port, @results) = @$ref;
print "$port : @results\n";
}
}
sub rpc
{
my ($ip, $port, $command) = @_;
async{
my $connection = ClarRPC->connect($ip, $port);
my @resp :shared = $connection->rpc(
'ClarRPCService::system_call', $command
);
$connection->disconnect();
unshift(@resp, $port);
$queue->enqueue(\@resp, undef);
}->detach
}
-
-
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] |
|
|
|
Re: Forking Clients
by Fletch (Bishop) on Oct 15, 2008 at 15:43 UTC
|
Using a framework such as POE and letting it take care of the nasty details makes this kind of thing much, much easier . . .
The cake is a lie.
The cake is a lie.
The cake is a lie.
| [reply] |
|
POE is a pretty awesome bit of work, a bit of a learning curve but worth it if you do stuff like that regularly.
- Ant
- Some of my
best work - (1 2 3)
| [reply] |
|
POE seems interesting, I will investigate it, but in the current situation it is probably not going to work since I need to avoid adding non-core Perl modules at all costs.
| [reply] |
Re: Forking Clients
by mr_mischief (Monsignor) on Oct 15, 2008 at 15:38 UTC
|
Does the information about non-blocking waits in waitpid help you any? If your platform supports it, that sounds like just what you need. | [reply] |
Re: Forking Clients
by JavaFan (Canon) on Oct 15, 2008 at 16:11 UTC
|
Well, if that's your program, you could just print @resp from the children.
If the point is that the parent needs all the data from @resp, you could instead of doing a fork, do a pipe open (using '|-' or '-|' -- I always forget which one is which). Have the child processes write @resp to the pipe; the parent can use a select loop (or use IO::Select) and read from the pipe. Or you could use shared memory to pass the information back. Alternatively, you don't use subprocesses, but threads and share some variables. | [reply] |
Re: Forking Clients
by AnomalousMonk (Archbishop) on Oct 15, 2008 at 23:09 UTC
|
Is gepapa aware that the OPed code has the following line:
$pid, @resp = $connection->rpc('ClarRPCService::system_call', $command
+);
If this highly suspicious statement is just, as I assume, a cut/paste typo, can gepapa please update the OP?
If this is a valid statement, can someone please explain to a forking tyro such as myself just what is going on here? | [reply] [d/l] |
|
The lexical variable $pid is evaluated and the result is then discarded. The variable is not tie-d to anything, so there can be no side effect. Don't you see a warning from this (although you don't use warnings; in your sample code)? All output from the $connection->rpc() function call is assigned to @resp. What's the point of having $pid in the statement? A typo or a thinko is strongly suggested.
>perl -wMstrict -le
"my $pid = 'foo';
my @resp = qw{ a b c };
sub S { return qw{ FOO bar quux } }
$pid, @resp = S();
print qq{$pid @resp};
"
Useless use of private variable in void context at -e line 1.
foo FOO bar quux
| [reply] [d/l] [select] |
|
I added the $pid part last second, it should have parens around the two variables I will fix it. I didn't really pay attention to it, I fixed the OP
Thanks.
| [reply] |
|
Hi,
What is your main worry with the statement? I guess I am not seeing it.
| [reply] |
|
|