]> git.vpit.fr Git - perl/modules/IPC-MorseSignals.git/commitdiff
Importing IPC-MorseSignals-0.07.tar.gz v0.07
authorVincent Pit <vince@profvince.com>
Sun, 29 Jun 2008 16:35:36 +0000 (18:35 +0200)
committerVincent Pit <vince@profvince.com>
Sun, 29 Jun 2008 16:35:36 +0000 (18:35 +0200)
14 files changed:
Changes
MANIFEST
META.yml
Makefile.PL
README
lib/IPC/MorseSignals.pm
t/00-load.t
t/01-import.t
t/02-sigusr.t
t/10-proto.t
t/11-ascii.t
t/12-unicode.t
t/13-speed.t
t/lib/IPCMTest.pm [new file with mode: 0644]

diff --git a/Changes b/Changes
index 096dd93aee22e615d5fb7bd50048748dce744eb7..dea81152ea452abecb7ac1c6192d91c63b680f4a 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,10 @@
 Revision history for IPC-MorseSignals
 
+0.07    2007-08-28 11:30 UTC
+        + Chg : Common test code was factored into a module.
+        + Chg : Tests were lightened again.
+        + Doc : Typos in POD.
+
 0.06    2007-08-21 08:15 UTC
         + Add : The protocol now carries the UTF-8 flag (hence you no longer
                 need to specify it to mrecv()) and the sender's PID (but you can
index f1f7a2b9294832655c2eb8b593971ec7189fe98a..44f3e0e17492cf70766ab6d398b4e68512310627 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -17,3 +17,4 @@ t/boilerplate.t
 t/kwalitee.t
 t/pod-coverage.t
 t/pod.t
+t/lib/IPCMTest.pm
index 9c2a7af54e79152903bb22d0c769ccaf296e229f..348e2928e19e552b2b9ac5a937ff6cf2fec5d677 100644 (file)
--- a/META.yml
+++ b/META.yml
@@ -1,12 +1,13 @@
 --- #YAML:1.0
 name:                IPC-MorseSignals
-version:             0.06
+version:             0.07
 abstract:            Communicate between processes with Morse signals.
 license:             perl
 generated_by:        ExtUtils::MakeMaker version 6.36
 distribution_type:   module
 requires:     
     Carp:                          0
+    Exporter:                      0
     POSIX:                         0
     Test::More:                    0
     Time::HiRes:                   0
index 4a1f9459db89f754069f0c16777c8373574373a1..2366dc91f07aca84da3b1ec4c5320bc98ce0aa76 100644 (file)
@@ -29,6 +29,7 @@ WriteMakefile(
     PL_FILES            => {},
     PREREQ_PM => {
         'Carp'        => 0,
+        'Exporter'    => 0,
         'POSIX'       => 0,
         'Test::More'  => 0,
         'Time::HiRes' => 0,
diff --git a/README b/README
index 098eddf457353ae2aa83e48538170180019c97c6..4cc7af916d3a5caff16c75b3f7cb99de2cb2f3e5 100644 (file)
--- a/README
+++ b/README
@@ -2,7 +2,7 @@ NAME
     IPC::MorseSignals - Communicate between processes with Morse signals.
 
 VERSION
-    Version 0.06
+    Version 0.07
 
 SYNOPSIS
         use IPC::MorseSignals qw/msend mrecv/;
@@ -49,7 +49,7 @@ FUNCTIONS
     the callback to trigger each time a complete message has arrived.
     Basically, you want to use it like this :
 
-        my $rv = mrecv local %SIG, cb => sub { ... };
+        my $rcv = mrecv local %SIG, cb => sub { ... };
 
     In the callback, $_[0] is the sender's PID (or 0 if the sender wanted to
     stay anonymous) and $_[1] is the message received.
@@ -66,7 +66,7 @@ FUNCTIONS
     or false otherwise.
 
   "mlastsender"
-        mlastmsg $rcv
+        mlastsender $rcv
 
     Holds the PID of the last process that sent data to the receiver $rcv, 0
     if that process was anonymous, or "undef" if no message has arrived yet.
index 098dfba19fdf843d81e5a50e809fdb7df9eeb512..e9ef591b2212ad8e3efe64b4062bb3a30d3dd8ce 100644 (file)
@@ -17,11 +17,11 @@ IPC::MorseSignals - Communicate between processes with Morse signals.
 
 =head1 VERSION
 
-Version 0.06
+Version 0.07
 
 =cut
 
-our $VERSION = '0.06';
+our $VERSION = '0.07';
 
 =head1 SYNOPSIS
 
@@ -114,7 +114,7 @@ sub msend {
 
 Takes as its first argument the C<%SIG> hash and returns a hash reference that represent the current state of the receiver. C<%SIG>'s fields C<'USR1'> and C<'USR2'> will be replaced by the receiver's callbacks. C<cb> specifies the callback to trigger each time a complete message has arrived. Basically, you want to use it like this :
 
-    my $rv = mrecv local %SIG, cb => sub { ... };
+    my $rcv = mrecv local %SIG, cb => sub { ... };
 
 In the callback, C<$_[0]> is the sender's PID (or C<0> if the sender wanted to stay anonymous) and C<$_[1]> is the message received.
 
@@ -193,6 +193,7 @@ Resets the state of the receiver C<$rcv>. Useful to abort transfers.
 
 sub mreset {
  my ($rcv) = @_;
+ croak 'Invalid receiver' unless defined $rcv;
  @{$rcv}{qw/state c n bits end utf8 sign/} = (0, undef, 0, '', '', 0, 0);
 }
 
@@ -206,12 +207,13 @@ Returns true if the receiver C<$rcv> is currently busy with incoming data, or fa
 
 sub mbusy {
  my ($rcv) = @_;
+ croak 'Invalid receiver' unless defined $rcv;
  return $rcv->{state} > 0;
 }
 
 =head2 C<mlastsender>
 
-    mlastmsg $rcv
+    mlastsender $rcv
 
 Holds the PID of the last process that sent data to the receiver C<$rcv>, C<0> if that process was anonymous, or C<undef> if no message has arrived yet. It isn't cleared by L</mreset>.
 
@@ -219,6 +221,7 @@ Holds the PID of the last process that sent data to the receiver C<$rcv>, C<0> i
 
 sub mlastsender {
  my ($rcv) = @_;
+ croak 'Invalid receiver' unless defined $rcv;
  return $rcv->{sender};
 }
 
@@ -232,6 +235,7 @@ Holds the last message received by C<$rcv>, or C<undef> if no message has arrive
 
 sub mlastmsg {
  my ($rcv) = @_;
+ croak 'Invalid receiver' unless defined $rcv;
  return $rcv->{msg};
 }
 
index 0138a8061bb39febc7dda9c0ab0872bafa05d4d4..fbef7823f99d0fdeeb935947e2e3bb34a5b48f9d 100644 (file)
@@ -1,5 +1,8 @@
 #!perl -T
 
+use strict;
+use warnings;
+
 use Test::More tests => 1;
 
 BEGIN {
index 071e5e45db6e11157850ca0113be85cc0875bc7c..ab6dd3a12eeb1e7e6664e01eb98e733eddd763c6 100644 (file)
@@ -1,5 +1,8 @@
 #!perl -T
 
+use strict;
+use warnings;
+
 use Test::More tests => 6;
 
 require IPC::MorseSignals;
index 112f5e6b11464945b929c3df5ee7bd733668b7f2..bfe2dc29ed9f56d198b1265c9a9a958ad89bf151 100644 (file)
@@ -1,8 +1,11 @@
 #!perl -T
 
+use strict;
+use warnings;
+
 use Test::More tests => 2;
 
-use POSIX qw/SIGINT SIGTERM SIGKILL EXIT_SUCCESS EXIT_FAILURE WIFEXITED WEXITSTATUS/;
+use POSIX qw/SIGUSR1 SIGUSR2/;
 
 my ($a, $b) = (0, 0);
 
index 307e17bdac2165909b39048b88168591977e4e0e..d05c7efc515455cd3e153abd44a843b0a89ccc70 100644 (file)
@@ -1,84 +1,16 @@
 #!perl -T
 
-use Test::More tests => 2;
-
-use POSIX qw/SIGINT SIGTERM SIGKILL SIGHUP EXIT_FAILURE/;
-
-use IPC::MorseSignals qw/msend mrecv mreset/;
+use strict;
+use warnings;
 
-my $lives = 5;
-
-sub spawn {
- --$lives;
- die 'forked too many times' if $lives < 0;
- pipe my $rdr, my $wtr or die "pipe() failed: $!";
- my $pid = fork;
- if (!defined $pid) {
-  die "fork() failed: $!";
- } elsif ($pid == 0) {
-  close $rdr or die "close() failed: $!";
-  my $block = 0;
-  my $s = mrecv local %SIG, cb => sub {
-   if ($block) {
-    $block = 0;
-   } else {
-    select $wtr; $| = 1;
-    print $wtr $_[0], ':', $_[1], "\n";
-    select $wtr; $| = 1;
-   }
-  };
-  $SIG{HUP} = sub { mreset $s };
-  $SIG{__WARN__} = sub { $block = 1 };
-  1 while 1;
-  exit EXIT_FAILURE;
- }
- close $wtr or die "close() failed: $!";
- return ($pid, $rdr);
-}
-
-sub slaughter {
- my ($pid) = @_;
- kill SIGINT  => $pid;
- kill SIGTERM => $pid;
- kill SIGKILL => $pid;
- waitpid $pid, 0;
-}
+use Test::More tests => 2;
 
-my ($pid, $rdr) = spawn;
+use lib 't/lib';
+use IPCMTest qw/try init cleanup/;
 
-sub trysend {
- my ($sign, $desc) = @_;
- my $speed = 2 ** 16;
- my $ok = 0;
- while (!$ok && (($speed /= 2) >= 1)) {
-  my $r = '';
-  eval {
-   local $SIG{ALRM} = sub { die 'timeout' };
-   local $SIG{__WARN__} = sub { die 'do not want warnings' };
-   my $a = (int(300 / $speed) || 1);
-   $a = 10 if $a > 10;
-   alarm $a;
-   kill SIGHUP => $pid;
-   msend 'x' => $pid, speed => $speed, sign => $sign;
-   $r = <$rdr>;
-  };
-  kill SIGHUP => $pid if $@;
-  alarm 0;
-  if (!defined $r) { # Something bad happened, respawn
-   close $rdr or die "close() failed: $!";
-   slaughter $pid;
-   ($pid, $rdr) = spawn;
-   $speed *= 2;      # Retry this speed
-  } else {
-   chomp $r;
-   my ($p, $m) = split /:/, $r;
-   $ok = ($m eq 'x') && ($p == ($sign ? $$ : 0)) if defined $m and defined $p;
-  }
- }
- ok($ok, $desc);
-}
+init;
 
-trysend 0, 'anonymous';
-trysend 1, 'signed';
+ok(try('x', 0), 'anonymous');
+ok(try('x', 1), 'signed');
 
-slaughter $pid;
+cleanup;
index 53890ccb8a5b8c885e9b5123b29d9fa80442c1f9..b5cc073c012560b3b93afb41041064bfb55b65d7 100644 (file)
@@ -1,91 +1,18 @@
 #!perl -T
 
-use Test::More tests => 4 * 3;
+use strict;
+use warnings;
 
-use POSIX qw/SIGINT SIGTERM SIGKILL SIGHUP EXIT_FAILURE/;
+use Test::More tests => 4;
 
-use IPC::MorseSignals qw/msend mrecv mreset/;
+use lib 't/lib';
+use IPCMTest qw/try init cleanup/;
 
-my $lives = 5;
+init;
 
-sub spawn {
- --$lives;
- die 'forked too many times' if $lives < 0;
- pipe my $rdr, my $wtr or die "pipe() failed: $!";
- my $pid = fork;
- if (!defined $pid) {
-  die "fork() failed: $!";
- } elsif ($pid == 0) {
-  close $rdr or die "close() failed: $!";
-  my $block = 0;
-  my $s = mrecv local %SIG, cb => sub {
-   if ($block) {
-    $block = 0;
-   } else {
-    select $wtr; $| = 1;
-    print $wtr $_[1], "\n";
-    select $wtr; $| = 1;
-   }
-  };
-  $SIG{HUP} = sub { mreset $s };
-  $SIG{__WARN__} = sub { $block = 1 };
-  1 while 1;
-  exit EXIT_FAILURE;
- }
- close $wtr or die "close() failed: $!";
- return ($pid, $rdr);
-}
+ok(try('hello'), 'ascii');
+ok(try("\0" x 5), 'few bits');
+ok(try("\x{FF}" x 5), 'lots of bits');
+ok(try("a\0b"), 'null character');
 
-sub slaughter {
- my ($pid) = @_;
- kill SIGINT  => $pid;
- kill SIGTERM => $pid;
- kill SIGKILL => $pid;
- waitpid $pid, 0;
-}
-
-my ($pid, $rdr) = spawn;
-
-sub trysend {
- my ($msg, $desc) = @_;
- my $speed = 2 ** 16;
- my $ok = 0;
- while (!$ok && (($speed /= 2) >= 1)) {
-  my $r = '';
-  eval {
-   local $SIG{ALRM} = sub { die 'timeout' };
-   local $SIG{__WARN__} = sub { die 'do not want warnings' };
-   my $a = (int(100 * (3 * length $msg) / $speed) || 1);
-   $a = 10 if $a > 10;
-   alarm $a;
-   kill SIGHUP => $pid;
-   msend $msg => $pid, speed => $speed, sign => 0;
-   $r = <$rdr>;
-  };
-  kill SIGHUP => $pid if $@;
-  alarm 0;
-  if (!defined $r) { # Something bad happened, respawn
-   close $rdr or die "close() failed: $!";
-   slaughter $pid;
-   ($pid, $rdr) = spawn;
-   $speed *= 2;      # Retry this speed
-  } else {
-   chomp $r;
-   if ($r eq $msg) {
-    $ok = 1;
-   } else {
-    kill SIGHUP => $pid;
-   }
-  }
- }
- ok($ok, $desc);
-}
-
-for (1 .. 3) {
- trysend 'hello', 'ascii';
- trysend "\0" x 10, 'few bits';
- trysend "\x{FF}" x 10, 'lots of bits';
- trysend "a\0b", 'null character';
-}
-
-slaughter $pid;
+cleanup;
index 0d3af168fc0a18cbc26d14454405b6f2186f1c7d..77ce70c40782127c3ac3cad68eaeb7960a5ccdb3 100644 (file)
@@ -1,99 +1,23 @@
 #!perl -T
 
-use Test::More tests => 7 * 3;
+use strict;
+use warnings;
 
-use POSIX qw/SIGINT SIGTERM SIGKILL SIGHUP EXIT_FAILURE/;
-
-use IPC::MorseSignals qw/msend mrecv mreset/;
+use Test::More tests => 7;
 
 use utf8;
 
-my $lives = 5;
-
-sub spawn {
- --$lives;
- die 'forked too many times' if $lives < 0;
- pipe my $rdr, my $wtr or die "pipe() failed: $!";
- my $pid = fork; 
- if (!defined $pid) {
-  die "fork() failed: $!";
- } elsif ($pid == 0) {
-  close $rdr or die "close() failed: $!";
-  binmode $wtr, ':utf8';
-  my $block = 0;
-  my $s = mrecv local %SIG, cb => sub {
-   if ($block) {
-    $block = 0;
-   } else {
-    select $wtr; $| = 1;
-    print $wtr $_[1], "\n";
-    select $wtr; $| = 1;
-   }
-  };
-  $SIG{HUP} = sub { mreset $s };
-  $SIG{__WARN__} = sub { $block = 1 };
-  1 while 1;
-  exit EXIT_FAILURE;
- }
- close $wtr or die "close() failed: $!";
- binmode $rdr, ':utf8';
- return ($pid, $rdr);
-}
-
-sub slaughter {
- my ($pid) = @_;
- kill SIGINT  => $pid;
- kill SIGTERM => $pid;
- kill SIGKILL => $pid;
- waitpid $pid, 0;
-} 
-
-my ($pid, $rdr) = spawn;
+use lib 't/lib';
+use IPCMTest qw/try init cleanup/;
 
-sub trysend8 {
- my ($msg, $desc) = @_;
- my $speed = 2 ** 16;
- my $ok = 0;
- $desc .= ' (unicode)';
- while (!$ok && (($speed /= 2) >= 1)) {
-  my $r = '';
-  eval {
-   local $SIG{ALRM} = sub { die 'timeout' };
-   local $SIG{__WARN__} = sub { die 'do not want warnings' };
-   my $a = (int(100 * (3 * length $msg) / $speed) || 1);
-   $a = 10 if $a > 10;
-   alarm $a;
-   kill SIGHUP => $pid;
-   msend $msg => $pid, speed => $speed, utf8 => 1, sign => 0;
-   $r = <$rdr>;
-  };
-  kill SIGHUP => $pid if $@;
-  alarm 0;
-  if (!defined $r) { # Something bad happened, respawn
-   close $rdr or die "close() failed: $!";
-   slaughter $pid;
-   ($pid, $rdr) = spawn;
-   $speed *= 2;       # Retry this speed
-  } else {
-   chomp $r;
-   if ($r eq $msg) {
-    $ok = 1;
-   } else {
-    kill SIGHUP => $pid;
-   }
-  }
- }
- ok($ok, $desc);
-}
+init 1;
 
-for (1 .. 3) {
- trysend8 'hello', 'ascii';
- trysend8 "\0" x 10, 'few bits';
- trysend8 "\x{FF}" x 10, 'lots of bits';
- trysend8 "a\0b", 'null character';
- trysend8 'éàùçà', 'extended';
- trysend8 '€€€', 'unicode';
- trysend8 'a€bécàd€e', 'mixed';
-}
+ok(try('hello'), 'ascii');
+ok(try("\0" x 5), 'few bits');
+ok(try("\x{FF}" x 5), 'lots of bits');
+ok(try("a\0b"), 'null character');
+ok(try('éàùçà'), 'extended');
+ok(try('€€€'), 'unicode');
+ok(try('à€béd'), 'mixed');
 
-slaughter $pid;
+cleanup;
index f2409d1fc1cffe2378d683c4bcd4673820cd3f13..ee5a80ce00645818c0fba05605b6448a481d4e5f 100644 (file)
 #!perl -T
 
-use Test::More tests => 10;
+use strict;
+use warnings;
 
-use POSIX qw/SIGINT SIGTERM SIGKILL SIGHUP EXIT_SUCCESS EXIT_FAILURE/;
+use Test::More tests => 3;
 
-use IPC::MorseSignals qw/msend mrecv mreset/;
-
-my $lives = 10;
-
-sub spawn {
- --$lives;
- die 'forked too many times' if $lives < 0;
- pipe my $rdr, my $wtr or die "pipe() failed: $!";
- my $pid = fork;
- if (!defined $pid) {
-  die "fork() failed: $!";
- } elsif ($pid == 0) {
-  close $rdr or die "close() failed: $!";
-  my $block = 0;
-  my $s = mrecv local %SIG, cb => sub {
-   if ($block) {
-    $block = 0;
-   } else {
-    select $wtr; $| = 1;
-    print $wtr $_[1], "\n";
-    select $wtr; $| = 1;
-   }
-  };
-  $SIG{HUP} = sub { mreset $s };
-  $SIG{__WARN__} = sub { $block = 1; };
-  1 while 1;
-  exit EXIT_FAILURE;
- }
- close $wtr or die "close() failed: $!";
- return ($pid, $rdr);
-}
-
-sub slaughter {
- my ($pid) = @_;
- kill SIGINT  => $pid;
- kill SIGTERM => $pid;
- kill SIGKILL => $pid;
- waitpid $pid, 0;
-}
+use lib 't/lib';
+use IPCMTest qw/speed init cleanup/;
 
+my $diag = sub { diag @_ };
 my @res;
 
-my ($pid, $rdr) = spawn;
-
-sub tryspeed {
- my ($l, $n) = @_;
- my $speed = 2 ** 16;
- my $ok = 0;
- my @alpha = ('a' .. 'z');
- my $msg = join '', map { $alpha[rand @alpha] } 1 .. $l;
- my $desc_base = "$l bytes sent $n times";
- while (($ok < $n) && (($speed /= 2) >= 1)) {
-  my $desc = "$desc_base at $speed bits/s";
-  diag("try $desc...");
-TRY:
-  for (1 .. $n) {
-   my $r = '';
-   eval {
-    local $SIG{ALRM} = sub { die 'timeout' };
-    local $SIG{__WARN__} = sub { die 'do not want warnings' };
-    my $a = (int(100 * (3 * $l) / $speed) || 1);
-    $a = 10 if $a > 10;
-    alarm $a;
-    kill SIGHUP => $pid;
-    msend $msg => $pid, speed => $speed;
-    $r = <$rdr>;
-   };
-   kill SIGHUP => $pid if $@;
-   alarm 0;
-   if (!defined $r) { # Something bad happened, respawn
-    close $rdr or die "close() failed: $!";
-    slaughter $pid;
-    ($pid, $rdr) = spawn;
-    redo TRY;         # Retry this send
-   } else {
-    chomp $r;
-    if ($r eq $msg) {
-     ++$ok;
-    } else {
-     kill SIGHUP => $pid;
-     last TRY;
-    }
-   }
-  }
- }
- ok($ok >= $n, $desc_base);
- push @res, $desc_base . (($speed) ? ' at ' . $speed . ' bits/s' : ' failed');
-}
+init;
 
-tryspeed 4,   1;
-tryspeed 4,   4;
-tryspeed 4,   16;
-tryspeed 4,   64;
-tryspeed 16,  1;
-tryspeed 16,  4;
-tryspeed 16,  16;
-tryspeed 64,  1;
-tryspeed 64,  4;
-tryspeed 256, 1;
+ok(speed(4,  1, $diag, \@res));
+ok(speed(4,  4, $diag, \@res));
+ok(speed(16, 1, $diag, \@res));
 
-slaughter $pid;
+cleanup;
 
 diag '=== Summary ===';
 diag $_ for sort {
diff --git a/t/lib/IPCMTest.pm b/t/lib/IPCMTest.pm
new file mode 100644 (file)
index 0000000..33d006c
--- /dev/null
@@ -0,0 +1,143 @@
+package IPCMTest;
+
+use strict;
+use warnings;
+
+use POSIX qw/SIGINT SIGTERM SIGKILL SIGHUP EXIT_FAILURE/;
+
+use IPC::MorseSignals qw/msend mrecv mreset/;
+
+use base qw/Exporter/;
+
+our @EXPORT_OK = qw/try speed init cleanup/;
+
+our $lives = 10;
+
+my ($utf8, $pid, $rdr);
+
+sub spawn {
+ --$lives;
+ die 'forked too many times' if $lives < 0;
+ pipe $rdr, my $wtr or die "pipe() failed: $!";
+ $pid = fork;
+ if (!defined $pid) {
+  die "fork() failed: $!";
+ } elsif ($pid == 0) {
+  close $rdr or die "close() failed: $!";
+  binmode $wtr, ':utf8' if $utf8;
+  my $block = 0;
+  my $rcv = mrecv local %SIG, cb => sub {
+   if ($block) {
+    $block = 0;
+   } else {
+    select $wtr; $| = 1;
+    print $wtr $_[0], ':', $_[1], "\n";
+    select $wtr; $| = 1;
+   }
+  };
+  $SIG{HUP} = sub { mreset $rcv };
+  $SIG{__WARN__} = sub { $block = 1 };
+  1 while 1;
+  exit EXIT_FAILURE;
+ }
+ close $wtr or die "close() failed: $!";
+ binmode $rdr, ':utf8' if $utf8;
+}
+
+sub slaughter {
+ kill SIGINT  => $pid;
+ kill SIGTERM => $pid;
+ kill SIGKILL => $pid;
+ waitpid $pid, 0;
+}
+
+sub init {
+ $utf8 = $_[0] || 0;
+ spawn;
+}
+
+sub cleanup { slaughter }
+
+sub try {
+ my ($msg, $sign) = @_;
+ $sign ||= 0;
+ my $speed = 2 ** 16;
+ my $ok = 0;
+ my @ret;
+ while (!$ok && (($speed /= 2) >= 1)) {
+  my $r = '';
+  eval {
+   local $SIG{ALRM} = sub { die 'timeout' };
+   local $SIG{__WARN__} = sub { die 'do not want warnings' };
+   my $a = (int(100 * (3 * length $msg) / $speed) || 1);
+   $a = 10 if $a > 10;
+   alarm $a;
+   kill SIGHUP => $pid;
+   msend $msg => $pid, speed => $speed, utf8 => $utf8, sign => $sign;
+   $r = <$rdr>;
+  };
+  kill SIGHUP => $pid if $@;
+  alarm 0;
+  if (!defined $r) { # Something bad happened, respawn
+   close $rdr or die "close() failed: $!";
+   slaughter;
+   spawn;
+   $speed *= 2;      # Retry this speed
+  } else {
+   chomp $r;
+   if ($r eq ((($sign) ? $$ : 0) . ':' . $msg)) {
+    $ok = 1;
+   } else {
+    kill SIGHUP => $pid;
+   }
+  }
+ }
+ return ($ok) ? $speed : 0;
+}
+
+sub speed {
+ my ($l, $n, $diag, $res) = @_;
+ my $speed = 2 ** 16;
+ my $ok = 0;
+ my @alpha = ('a' .. 'z');
+ my $msg = join '', map { $alpha[rand @alpha] } 1 .. $l;
+ my $desc_base = "$l bytes sent $n times";
+ while (($ok < $n) && (($speed /= 2) >= 1)) {
+  my $desc = "$desc_base at $speed bits/s";
+  $diag->("try $desc...");
+TRY:
+  for (1 .. $n) {
+   my $r = '';
+   eval {
+    local $SIG{ALRM} = sub { die 'timeout' };
+    local $SIG{__WARN__} = sub { die 'do not want warnings' };
+    my $a = (int(100 * (3 * $l) / $speed) || 1);
+    $a = 10 if $a > 10;
+    alarm $a;
+    kill SIGHUP => $pid;
+    msend $msg => $pid, speed => $speed, utf8 => $utf8, sign => 0;
+    $r = <$rdr>;
+   };
+   kill SIGHUP => $pid if $@;
+   alarm 0;
+   if (!defined $r) { # Something bad happened, respawn
+    close $rdr or die "close() failed: $!";
+    slaughter;
+    spawn;
+    redo TRY;         # Retry this send
+   } else {
+    chomp $r;
+    if ($r eq '0:' . $msg) {
+     ++$ok;
+    } else {
+     kill SIGHUP => $pid;
+     last TRY;
+    }
+   }
+  }
+ }
+ push @$res, $desc_base . (($speed) ? ' at ' . $speed . ' bits/s' : ' failed');
+ return ($ok == $n);
+}
+
+1;