summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Schlichting <fsfs@debian.org>2014-05-01 22:06:03 +0200
committerFlorian Schlichting <fsfs@debian.org>2014-05-01 22:06:03 +0200
commit53ce1498e94ddd8cf6d5b68f692f7fec4599c7fa (patch)
tree229008064a7e8cf0f291fdf0c7df53a5d86d105d
parent9643b1eb7feaff7e5a488bb025183cf7e287ac16 (diff)
Imported Upstream version 1.513
-rw-r--r--CHANGES35
-rw-r--r--CREDITS6
-rw-r--r--MANIFEST2
-rw-r--r--META.yml40
-rw-r--r--Makefile.PL40
-rw-r--r--README2
-rwxr-xr-xexamples/pipe1.pl257
-rwxr-xr-xexamples/pipe2.pl338
-rwxr-xr-xexamples/utf8.pl4
-rw-r--r--lib/MCE.pm39
-rw-r--r--lib/MCE.pod2
-rw-r--r--lib/MCE/Core.pod149
-rw-r--r--lib/MCE/Core/Input/Generator.pm2
-rw-r--r--lib/MCE/Core/Input/Handle.pm2
-rw-r--r--lib/MCE/Core/Input/Iterator.pm2
-rw-r--r--lib/MCE/Core/Input/Request.pm2
-rw-r--r--lib/MCE/Core/Input/Sequence.pm2
-rw-r--r--lib/MCE/Core/Manager.pm2
-rw-r--r--lib/MCE/Core/Validation.pm2
-rw-r--r--lib/MCE/Core/Worker.pm2
-rw-r--r--lib/MCE/Examples.pod6
-rw-r--r--lib/MCE/Flow.pm4
-rw-r--r--lib/MCE/Grep.pm4
-rw-r--r--lib/MCE/Loop.pm4
-rw-r--r--lib/MCE/Map.pm4
-rw-r--r--lib/MCE/Queue.pm4
-rw-r--r--lib/MCE/Signal.pm92
-rw-r--r--lib/MCE/Step.pm4
-rw-r--r--lib/MCE/Stream.pm4
-rw-r--r--lib/MCE/Subs.pm4
-rw-r--r--lib/MCE/Util.pm4
31 files changed, 864 insertions, 200 deletions
diff --git a/CHANGES b/CHANGES
index 712aaf4..7e541c9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,41 @@
Revision history for MCE
+1.513 Sat Apr 19 20:30:00 EST 2014
+
+ [BUG FIXES]
+
+ * Added fix for bug RT#94869 -- crash when restarting workers with 9+
+ workers. Updated the perldoc for restart_worker in MCE::Core.pod.
+
+ [ENHANCEMENTS]
+
+ * Replaced $self with $mce in MCE::/Core.pod to be consistent with
+ examples described in MCE::Examples.pod.
+
+1.512 Fri Apr 18 21:00:00 EST 2014
+
+ [BUG FIXES]
+
+ * Signal-handling update for MCE::Signal. Passing the -setpgrp option is not
+ necessary, even with Daemon::Control. Piping data into and out is better
+ supported with this release (\*STDIN). This resolves bug RT#94706.
+
+ cat infile | mce_script | head
+ mce_script < infile | head
+
+ Added Shawn Halpenny to the CREDITS file.
+
+ * The utf8.pl example now runs under the BSD 9.0 environment. This was
+ failing due to $^H{charnames} is not defined error. Removed the constant
+ from the list of unicode characters inside the script.
+
+ [ENHANCEMENTS]
+
+ * Added examples/pipe1.pl and pipe2.pl. These process STDIN or FILE in
+ parallel. Processing is via Perl for pipe1.pl, whereas an external
+ command for pipe2.pl.
+
1.511 Fri Apr 04 22:30:00 EST 2014
[BUG FIXES]
diff --git a/CREDITS b/CREDITS
index f482128..87416c8 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1,6 +1,6 @@
All the people reporting problems and fixes or additions. More specifically in
-alphabetical order by last name:
+alphabetical order by last name.
###############################################################################
# * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * #
@@ -28,6 +28,10 @@ Oliver Gorwits
handler. Basically, eval { die 'this should not cause MCE to die' };
* Changed INIT { ... } to sub import { ... } inside MCE.pm.
+Shawn Halpenny
+ For reporting an issue (bug RT#94706) with signal handling in MCE::Signal.
+ Also, thank you for tip on getpgrp().
+
Philip Mabon
For reporting on a couple issues with MCE in particular bug RT#92627.
diff --git a/MANIFEST b/MANIFEST
index e3f6921..30e4dfb 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -50,6 +50,8 @@ examples/matmult/strassen_07_t.pl
examples/matmult/strassen_49_f.pl
examples/matmult/strassen_49_t.pl
examples/matmult/strassen_perl.pl
+examples/pipe1.pl
+examples/pipe2.pl
examples/scaling_pings.pl
examples/seq_demo.pl
examples/tbray/README
diff --git a/META.yml b/META.yml
index 5bebf4a..603d5bd 100644
--- a/META.yml
+++ b/META.yml
@@ -1,6 +1,6 @@
--- #YAML:1.0
name: MCE
-version: 1.511
+version: 1.513
abstract: Many-core Engine for Perl. Provides parallel processing capabilities.
author:
- Mario E. Roy <marioeroy AT gmail DOT com>
@@ -28,61 +28,61 @@ resources:
provides:
MCE:
file: lib/MCE.pm
- version: 1.511
+ version: 1.513
MCE::Core::Input::Generator:
file: lib/MCE/Core/Input/Generator.pm
- version: 1.511
+ version: 1.513
MCE::Core::Input::Handle:
file: lib/MCE/Core/Input/Handle.pm
- version: 1.511
+ version: 1.513
MCE::Core::Input::Iterator:
file: lib/MCE/Core/Input/Iterator.pm
- version: 1.511
+ version: 1.513
MCE::Core::Input::Request:
file: lib/MCE/Core/Input/Request.pm
- version: 1.511
+ version: 1.513
MCE::Core::Input::Sequence:
file: lib/MCE/Core/Input/Sequence.pm
- version: 1.511
+ version: 1.513
MCE::Core::Manager:
file: lib/MCE/Core/Manager.pm
- version: 1.511
+ version: 1.513
MCE::Core::Validation:
file: lib/MCE/Core/Validation.pm
- version: 1.511
+ version: 1.513
MCE::Core::Worker:
file: lib/MCE/Core/Worker.pm
- version: 1.511
+ version: 1.513
MCE::Flow:
file: lib/MCE/Flow.pm
- version: 1.511
+ version: 1.513
MCE::Grep:
file: lib/MCE/Grep.pm
- version: 1.511
+ version: 1.513
MCE::Loop:
file: lib/MCE/Loop.pm
- version: 1.511
+ version: 1.513
MCE::Map:
file: lib/MCE/Map.pm
- version: 1.511
+ version: 1.513
MCE::Queue:
file: lib/MCE/Queue.pm
- version: 1.511
+ version: 1.513
MCE::Signal:
file: lib/MCE/Signal.pm
- version: 1.511
+ version: 1.513
MCE::Step:
file: lib/MCE/Step.pm
- version: 1.511
+ version: 1.513
MCE::Stream:
file: lib/MCE/Stream.pm
- version: 1.511
+ version: 1.513
MCE::Subs:
file: lib/MCE/Subs.pm
- version: 1.511
+ version: 1.513
MCE::Util:
file: lib/MCE/Util.pm
- version: 1.511
+ version: 1.513
no_index:
directory:
- t
diff --git a/Makefile.PL b/Makefile.PL
index c975993..420247c 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -27,7 +27,7 @@ WriteMakefile(
'Time::HiRes' => 0
},
- VERSION => '1.511',
+ VERSION => '1.513',
EXE_FILES => [],
@@ -63,79 +63,79 @@ WriteMakefile(
provides => {
'MCE' => {
'file' => 'lib/MCE.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Core::Input::Generator' => {
'file' => 'lib/MCE/Core/Input/Generator.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Core::Input::Handle' => {
'file' => 'lib/MCE/Core/Input/Handle.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Core::Input::Iterator' => {
'file' => 'lib/MCE/Core/Input/Iterator.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Core::Input::Request' => {
'file' => 'lib/MCE/Core/Input/Request.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Core::Input::Sequence' => {
'file' => 'lib/MCE/Core/Input/Sequence.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Core::Manager' => {
'file' => 'lib/MCE/Core/Manager.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Core::Validation' => {
'file' => 'lib/MCE/Core/Validation.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Core::Worker' => {
'file' => 'lib/MCE/Core/Worker.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Flow' => {
'file' => 'lib/MCE/Flow.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Grep' => {
'file' => 'lib/MCE/Grep.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Loop' => {
'file' => 'lib/MCE/Loop.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Map' => {
'file' => 'lib/MCE/Map.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Queue' => {
'file' => 'lib/MCE/Queue.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Signal' => {
'file' => 'lib/MCE/Signal.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Step' => {
'file' => 'lib/MCE/Step.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Stream' => {
'file' => 'lib/MCE/Stream.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Subs' => {
'file' => 'lib/MCE/Subs.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
},
'MCE::Util' => {
'file' => 'lib/MCE/Util.pm',
- 'version' => '1.511'
+ 'version' => '1.513'
}
}
}) : ()),
diff --git a/README b/README
index 5e9ccd4..7e4306f 100644
--- a/README
+++ b/README
@@ -3,7 +3,7 @@
# * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * #
###############################################################################
-This document describes MCE version 1.511
+This document describes MCE version 1.513
Many-core Engine (MCE) for Perl helps enable a new level of performance by
maximizing all available cores. MCE spawns a pool of workers and therefore
diff --git a/examples/pipe1.pl b/examples/pipe1.pl
new file mode 100755
index 0000000..44f72bf
--- /dev/null
+++ b/examples/pipe1.pl
@@ -0,0 +1,257 @@
+#!/usr/bin/env perl
+###############################################################################
+## ----------------------------------------------------------------------------
+## Process STDIN or FILE via Perl in parallel.
+##
+## This is by no means a complete script, but rather a "how-to" for folks
+## wanting to create their own parallel script.
+##
+###############################################################################
+
+use strict;
+use warnings;
+
+my ($prog_name, $prog_dir, $base_dir);
+
+BEGIN {
+ use Cwd qw(abs_path);
+
+ $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g;
+ $prog_dir = abs_path($0); $prog_dir =~ s{[\\/][^\\/]*$}{};
+ $base_dir = $prog_dir; $base_dir =~ s{[\\/][^\\/]*$}{};
+
+ $ENV{PATH} = $prog_dir .($^O eq 'MSWin32' ? ';' : ':'). $ENV{PATH};
+
+ unshift @INC, "$base_dir/lib";
+}
+
+use Getopt::Long qw(
+ :config bundling pass_through no_ignore_case no_auto_abbrev
+);
+
+use Scalar::Util qw( looks_like_number );
+use Fcntl qw( O_RDONLY );
+
+use MCE::Signal qw( -use_dev_shm );
+use MCE::Loop;
+
+###############################################################################
+## ----------------------------------------------------------------------------
+## Display usage and exit.
+##
+###############################################################################
+
+sub usage {
+
+ print STDERR <<"::_USAGE_BLOCK_END_::";
+
+NAME
+ $prog_name -- process STDIN or FILE via Perl in parallel
+
+SYNOPSIS
+ $prog_name [script_options] [FILE]
+
+DESCRIPTION
+ The $prog_name script processes STDIN or FILE in parallel. STDIN is read
+ unless FILE is specified. Specifing more than 1 file will error.
+
+ The following options are available:
+
+ --RS RECORD_SEPARATOR
+ Input record separator -- default: newline
+
+ --chunk-size CHUNK_SIZE
+ Specify chunk size for MCE -- default: auto
+ Can also take a suffix; K/k (kilobytes) or M/m (megabytes).
+
+ Less than or equal to 8192 is the number of records.
+ Greater than 8192 is the number of bytes. The maximum
+ is 24m by MCE internally.
+
+ --max-workers MAX_WORKERS
+ Specify number of workers for MCE -- default: 8
+
+ --parallel-io
+ Enable parallel IO for FILE. This is not recommended if running
+ on several nodes simultaneously and reading from the same shared
+ storage.
+
+EXIT STATUS
+ $prog_name exits 0 on success, and >0 if an error occurs.
+
+EXAMPLES
+ Process STDIN (workers request the next chunk from the manager process).
+
+ cat infile | $prog_name --chunk-size=2k >out 2>err
+ $prog_name --chunk-size=2k < infile >out 2>err
+
+ Process FILE (workers communicate the next offset among themselves).
+
+ $prog_name --chunk-size=2k infile >out 2>err
+
+::_USAGE_BLOCK_END_::
+
+ exit 1;
+}
+
+###############################################################################
+## ----------------------------------------------------------------------------
+## Define defaults and process command-line arguments. Determine input stream.
+##
+###############################################################################
+
+my $RS = $/;
+my $chunk_size = 'auto';
+my $max_workers = 8;
+my $parallel_io = 0;
+
+{
+ local $SIG{__WARN__} = sub { };
+
+ GetOptions(
+ 'RS=s' => \$RS,
+ 'chunk-size|chunk_size=s' => \$chunk_size,
+ 'max-workers|max_workers=s' => \$max_workers,
+ 'parallel-io|parallel_io' => \$parallel_io
+ );
+
+ if ($max_workers !~ /^auto/) {
+ unless (looks_like_number($max_workers) && $max_workers > 0) {
+ print STDERR "$prog_name: $max_workers: invalid max workers\n";
+ exit 2;
+ }
+ }
+
+ if ($chunk_size !~ /^auto/) {
+ if ($chunk_size =~ /^(\d+)K/i) {
+ $chunk_size = $1 * 1024;
+ }
+ elsif ($chunk_size =~ /^(\d+)M/i) {
+ $chunk_size = $1 * 1024 * 1024;
+ }
+
+ if (!looks_like_number($chunk_size) || $chunk_size < 1) {
+ print STDERR "$prog_name: $chunk_size: invalid chunk size\n";
+ exit 2;
+ }
+ }
+}
+
+usage() if (@ARGV > 1);
+
+my $input = (defined $ARGV[0]) ? $ARGV[0] : \*STDIN;
+
+if (ref $input eq '') {
+ if (! -e $input) {
+ print STDERR "$prog_name: $input: No such file or directory\n";
+ exit 2;
+ }
+ if (-d $input) {
+ print STDERR "$prog_name: $input: Is a directory\n";
+ exit 2;
+ }
+}
+
+###############################################################################
+## ----------------------------------------------------------------------------
+## Output function. Define the gather iterator for preserving output order.
+##
+###############################################################################
+
+my $buf = sprintf('%65536s', ''); ## Create a continuous buffer for the
+my $exit_status = 0; ## output routine.
+
+sub output {
+
+ my ($file, $sendto_fh) = @_;
+ my ($fh, $n_read);
+
+ if (-s $file) {
+ sysopen($fh, $file, O_RDONLY);
+
+ while (1) {
+ $n_read = sysread($fh, $buf, 65536);
+ last if $n_read == 0;
+
+ syswrite($sendto_fh, $buf, $n_read);
+ }
+
+ close $fh;
+ }
+
+ unlink $file;
+}
+
+sub gather_iterator {
+
+ my ($out_fh, $err_fh) = @_;
+ my (%tmp, $path); my $order_id = 1;
+
+ return sub {
+
+ $tmp{$_[0]} = $_[1];
+ $exit_status = $_[2] if ($_[2] > $exit_status);
+
+ while (1) {
+ last unless exists $tmp{$order_id};
+
+ $path = $tmp{$order_id};
+ output("$path.err", $err_fh);
+ output("$path.out", $out_fh);
+
+ delete $tmp{$order_id++};
+ }
+ };
+}
+
+###############################################################################
+## ----------------------------------------------------------------------------
+## Configure MCE. Process STDIN in parallel afterwards. The mce_loop_f routine
+## can take a GLOB reference or a scalar containing the path to the file.
+##
+###############################################################################
+
+MCE::Loop::init {
+
+ RS => $RS, use_slurpio => 1, parallel_io => $parallel_io,
+ chunk_size => $chunk_size, max_workers => $max_workers,
+
+ gather => gather_iterator(\*STDOUT, \*STDERR)
+};
+
+mce_loop_f {
+
+ my ($mce, $chunk_ref, $chunk_id) = @_;
+ my $path = MCE->tmp_dir() .'/'. $chunk_id;
+ my $chunk_status = 0;
+
+ open my $out_fh, ">", "$path.out";
+ open my $err_fh, ">", "$path.err";
+
+ ## open my $mem_fh, "<", $chunk_ref; ## $chunk_ref is a scalar ref
+ ## ## when use_slurpio => 1
+ ## while (<$mem_fh>) {
+ ## print $out_fh $_; ## Consider appending to an array.
+ ## } ## Then write to output handle.
+ ##
+ ## close $mem_fh;
+
+ print $out_fh $$chunk_ref; ## (or) write entire chunk
+
+ close $out_fh;
+ close $err_fh;
+
+ MCE->gather($chunk_id, $path, $chunk_status);
+
+} $input;
+
+###############################################################################
+## ----------------------------------------------------------------------------
+## Cleanup and exit.
+##
+###############################################################################
+
+MCE::Loop::finish();
+
+exit $exit_status;
+
diff --git a/examples/pipe2.pl b/examples/pipe2.pl
new file mode 100755
index 0000000..20d3675
--- /dev/null
+++ b/examples/pipe2.pl
@@ -0,0 +1,338 @@
+#!/usr/bin/env perl
+###############################################################################
+## ----------------------------------------------------------------------------
+## Process STDIN or FILE via an external command in parallel.
+##
+## This is by no means a complete script, but rather a "how-to" for folks
+## wanting to create their own parallel script.
+##
+## For UNIX, the Perl interpretor does "not" sub-shell when running command.
+##
+###############################################################################
+
+use strict;
+use warnings;
+
+my ($prog_name, $prog_dir, $base_dir);
+
+BEGIN {
+ use Cwd qw(abs_path);
+
+ $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g;
+ $prog_dir = abs_path($0); $prog_dir =~ s{[\\/][^\\/]*$}{};
+ $base_dir = $prog_dir; $base_dir =~ s{[\\/][^\\/]*$}{};
+
+ $ENV{PATH} = $prog_dir .($^O eq 'MSWin32' ? ';' : ':'). $ENV{PATH};
+
+ unshift @INC, "$base_dir/lib";
+}
+
+use Getopt::Long qw(
+ :config bundling pass_through no_ignore_case no_auto_abbrev
+);
+
+use Scalar::Util qw( looks_like_number );
+use Fcntl qw( O_RDONLY );
+
+use MCE::Signal qw( -use_dev_shm );
+use MCE::Loop;
+
+###############################################################################
+## ----------------------------------------------------------------------------
+## Display usage and exit.
+##
+###############################################################################
+
+sub usage {
+
+ print STDERR <<"::_USAGE_BLOCK_END_::";
+
+NAME
+ $prog_name -- process STDIN or FILE via an external command in parallel
+
+SYNOPSIS
+ $prog_name [script_options] command [command_options] [FILE]
+
+DESCRIPTION
+ The $prog_name script processes STDIN or FILE in parallel. STDIN is read
+ unless FILE is specified. Specifing more than 1 file will error.
+
+ The following options are available:
+
+ --RS RECORD_SEPARATOR
+ Input record separator -- default: newline
+
+ --abort-on-err
+ Notify MCE to abort if command exited with a non-zero exit status.
+ Workers will stop taking new work causing MCE to terminate early.
+
+ --chunk-size CHUNK_SIZE
+ Specify chunk size for MCE -- default: auto
+ Can also take a suffix; K/k (kilobytes) or M/m (megabytes).
+
+ Less than or equal to 8192 is the number of records.
+ Greater than 8192 is the number of bytes. The maximum
+ is 24m by MCE internally.
+
+ --max-workers MAX_WORKERS
+ Specify number of workers for MCE -- default: 8
+
+ --parallel-io
+ Enable parallel IO for FILE. This is not recommended if running
+ on several nodes simultaneously and reading from the same shared
+ storage.
+
+EXIT STATUS
+ $prog_name exits 0 on success, and >0 if an error occurs.
+
+EXAMPLES
+ The "command" receives data from STDIN. Notice the map and reduce idiom
+ for wc -l below.
+
+ Process STDIN (workers request the next chunk from the manager process).
+
+ cat infile | $prog_name --chunk-size=2k cat >out 2>err
+ $prog_name --chunk-size=2k cat < infile >out 2>err
+
+ $prog_name --chunk-size=8m wc -l < largefile | \
+ awk '{ lines = lines + \$1 } END { print lines }'
+
+ Process FILE (workers communicate the next offset among themselves).
+
+ $prog_name --chunk-size=2k cat infile >out 2>err
+
+ $prog_name --parallel-io --chunk-size=8m wc -l largefile | \
+ awk '{ lines = lines + \$1 } END { print lines }'
+
+ $prog_name --chunk-size=8m wc -l largefile | \
+ awk '{ lines = lines + \$1 } END { print lines }'
+
+::_USAGE_BLOCK_END_::
+
+ exit 1;
+}
+
+###############################################################################
+## ----------------------------------------------------------------------------
+## Define defaults and process command-line arguments.
+##
+###############################################################################
+
+my $RS = $/;
+my $abort_on_err = 0;
+my $chunk_size = 'auto';
+my $max_workers = 8;
+my $parallel_io = 0;
+
+{
+ local $SIG{__WARN__} = sub { };
+
+ GetOptions(
+ 'RS=s' => \$RS,
+ 'abort-on-err|abort_on_err' => \$abort_on_err,
+ 'chunk-size|chunk_size=s' => \$chunk_size,
+ 'max-workers|max_workers=s' => \$max_workers,
+ 'parallel-io|parallel_io' => \$parallel_io
+ );
+
+ if ($max_workers !~ /^auto/) {
+ unless (looks_like_number($max_workers) && $max_workers > 0) {
+ print STDERR "$prog_name: $max_workers: invalid max workers\n";
+ exit 2;
+ }
+ }
+
+ if ($chunk_size !~ /^auto/) {
+ if ($chunk_size =~ /^(\d+)K/i) {
+ $chunk_size = $1 * 1024;
+ }
+ elsif ($chunk_size =~ /^(\d+)M/i) {
+ $chunk_size = $1 * 1024 * 1024;
+ }
+
+ if (!looks_like_number($chunk_size) || $chunk_size < 1) {
+ print STDERR "$prog_name: $chunk_size: invalid chunk size\n";
+ exit 2;
+ }
+ }
+}
+
+usage() unless @ARGV;
+usage() if $ARGV[0] =~ /^-/;
+
+###############################################################################
+## ----------------------------------------------------------------------------
+## Exit if "command" is not found. Determine input stream.
+##
+###############################################################################
+
+my $is_MSWin32 = $^O eq 'MSWin32';
+my ($cmd_name, $cmd_path);
+
+$cmd_name = shift @ARGV;
+
+{
+ my $pth_sep = $is_MSWin32 ? ";" : ":";
+ my $dir_sep = $is_MSWin32 ? "\\" : "/";
+
+ for ( split ${pth_sep}, $ENV{'PATH'} ) {
+ if (-e "$_${dir_sep}${cmd_name}" || -e "$_${dir_sep}${cmd_name}.exe") {
+ $cmd_path = "$_${dir_sep}${cmd_name}";
+ last;
+ }
+ }
+}
+
+if (! defined $cmd_path) {
+ print STDERR "$prog_name: $cmd_name: command not found\n";
+ exit 2;
+}
+if (! $is_MSWin32 && ! -x $cmd_path) {
+ print STDERR "$prog_name: $cmd_name: command is not executable\n";
+ exit 2;
+}
+
+my $input = \*STDIN;
+
+if (defined $ARGV[-1] && $ARGV[-1] !~ /^-/) {
+ usage() if (defined $ARGV[-2] && $ARGV[-2] !~ /^-/);
+ $input = pop(@ARGV);
+}
+
+if (ref $input eq '') {
+ if (! -e $input) {
+ print STDERR "$prog_name: $input: No such file or directory\n";
+ exit 2;
+ }
+ if (-d $input) {
+ print STDERR "$prog_name: $input: Is a directory\n";
+ exit 2;
+ }
+}
+
+###############################################################################
+## ----------------------------------------------------------------------------
+## Output function. Define the gather iterator for preserving output order.
+##
+###############################################################################
+
+my $buf = sprintf('%65536s', ''); ## Create a continuous buffer for the
+my $exit_status = 0; ## output routine.
+
+sub output {
+
+ my ($file, $sendto_fh) = @_;
+ my ($fh, $n_read);
+
+ if (-s $file) {
+ sysopen($fh, $file, O_RDONLY);
+
+ while (1) {
+ $n_read = sysread($fh, $buf, 65536);
+ last if $n_read == 0;
+
+ syswrite($sendto_fh, $buf, $n_read);
+ }
+
+ close $fh;
+ }
+
+ unlink $file;
+}
+
+sub gather_iterator {
+
+ my ($out_fh, $err_fh) = @_;
+ my (%tmp, $path); my $order_id = 1;
+
+ return sub {
+
+ $tmp{$_[0]} = $_[1];
+ $exit_status = $_[2] if ($_[2] > $exit_status);
+
+ MCE->abort() if ($abort_on_err && $_[2]);
+
+ while (1) {
+ last unless exists $tmp{$order_id};
+
+ $path = $tmp{$order_id};
+ output("$path.err", $err_fh);
+ output("$path.out", $out_fh);
+
+ delete $tmp{$order_id++};
+ }
+ };
+}
+
+###############################################################################
+## ----------------------------------------------------------------------------
+## Configure MCE. Process STDIN in parallel afterwards. The mce_loop_f routine
+## can take a GLOB reference or a scalar containing the path to the file.
+##
+###############################################################################
+
+MCE::Loop::init {
+
+ RS => $RS, use_slurpio => 1, parallel_io => $parallel_io,
+ chunk_size => $chunk_size, max_workers => $max_workers,
+
+ gather => gather_iterator(\*STDOUT, \*STDERR)
+};
+
+mce_loop_f {
+
+ my ($mce, $chunk_ref, $chunk_id) = @_;
+ my $path = MCE->tmp_dir() .'/'. $chunk_id;
+ local ($!, $?);
+
+ if ($is_MSWin32) {
+ $path =~ s{/}{\\\\}g;
+
+ open my $out_fh, "+>:raw", "$path.in";
+ print $out_fh $$chunk_ref;
+ close $out_fh;
+
+ system("$cmd_path @ARGV < $path.in > $path.out 2> $path.err");
+
+ unlink "$path.in";
+ }
+ else {
+ ## Borrowed bits from IPC::Run3 for STDOUT/ERR. For STDIN, went with
+ ## open $cmd_fh, '|-', ... due to lesser overhead behind the scene.
+
+ local (*STDOUT_SAVE, *STDERR_SAVE);
+ my ($out_fh, $err_fh, $cmd_fh);
+
+ open STDOUT_SAVE, '>&STDOUT';
+ open $out_fh, '+>:raw', "$path.out";
+ open STDOUT, '>&' . fileno $out_fh;
+
+ open STDERR_SAVE, '>&STDERR';
+ open $err_fh, '+>:raw', "$path.err";
+ open STDERR, '>&' . fileno $err_fh;
+
+ ## Seeing "maximal count of pending signals (NUM) exceeded" message.
+ ## Therefore using syswrite instead of print below.
+
+ open $cmd_fh, '|-', $cmd_path, @ARGV; ## Spawn external command
+ syswrite $cmd_fh, $$chunk_ref; ## Write to external's STDIN
+ close $cmd_fh;
+
+ open STDOUT, '>&STDOUT_SAVE'; close $out_fh;
+ open STDERR, '>&STDERR_SAVE'; close $err_fh;
+ }
+
+ MCE->gather($chunk_id, $path, ${^CHILD_ERROR_NATIVE} >> 8);
+
+} $input;
+
+###############################################################################
+## ----------------------------------------------------------------------------
+## Cleanup and exit.
+##
+###############################################################################
+
+MCE::Loop::finish();
+
+exit $exit_status;
+
diff --git a/examples/utf8.pl b/examples/utf8.pl
index cdab7f3..bea3401 100755
--- a/examples/utf8.pl
+++ b/examples/utf8.pl
@@ -28,7 +28,9 @@ use open qw(:utf8 :std);
## - Once after fetching results from MCE->gather()
## Some Unicode characters from Basic Latin, Latin-1, and beyond.
-my @list = (qw(U Ö Å Ǣ Ȝ), "\N{INTERROBANG}");
+## my @list = (qw(U Ö Å Ǣ Ȝ), "\N{INTERROBANG}");
+
+my @list = qw(U Ö Å Ǣ Ȝ);
print "0: for-loop: $_\n" for (@list);
print "\n";
diff --git a/lib/MCE.pm b/lib/MCE.pm
index 37c0210..201826c 100644
--- a/lib/MCE.pm
+++ b/lib/MCE.pm
@@ -18,7 +18,7 @@ use Time::HiRes qw( time );
use MCE::Signal;
use bytes;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
our (%_valid_fields_new, %_params_allowed_args, %_valid_fields_task);
our ($_is_cygwin, $_is_MSWin32, $_is_WinEnv);
@@ -848,7 +848,7 @@ sub restart_worker {
_dispatch_child($self, $_wid, $_task, $_task_id, $_task_wid, $_params);
}
- select(undef, undef, undef, 0.002);
+ select(undef, undef, undef, 0.001);
return;
}
@@ -1313,10 +1313,11 @@ sub shutdown {
## Remove the session directory.
if (defined $_sess_dir) {
- unlink "$_sess_dir/_dat.lock.1";
+ unlink "$_sess_dir/_dat.lock.e"
+ if (-e "$_sess_dir/_dat.lock.e");
if ($_lock_chn) {
- unlink "$_sess_dir/_dat.lock.$_" for (2 .. $_data_channels);
+ unlink "$_sess_dir/_dat.lock.$_" for (1 .. $_data_channels);
}
unlink "$_sess_dir/_com.lock";
rmdir "$_sess_dir";
@@ -1479,25 +1480,21 @@ sub exit {
my $_task_id = $self->{_task_id};
my $_sess_dir = $self->{_sess_dir};
- if (!$_lock_chn || $_chn != 1) {
- if (defined $_DAT_LOCK) {
- close $_DAT_LOCK; undef $_DAT_LOCK;
- select(undef, undef, undef, 0.002);
- }
- open $_DAT_LOCK, '+>>:raw:stdio', "$_sess_dir/_dat.lock.1"
- or die "(W) open error $_sess_dir/_dat.lock.1: $!\n";
- }
-
unless ($self->{_exiting}) {
$self->{_exiting} = 1;
+ local $\ = undef if (defined $\);
my $_len = length $_exit_msg;
- local $\ = undef;
$_exit_id =~ s/[\r\n][\r\n]*/ /mg;
- flock $_DAT_LOCK, LOCK_EX;
- select(undef, undef, undef, 0.02) if ($_is_cygwin);
+ open my $_DAE_LOCK, '+>>:raw:stdio', "$_sess_dir/_dat.lock.e"
+ or die "(W) open error $_sess_dir/_dat.lock.e: $!\n";
+
+ flock $_DAE_LOCK, LOCK_EX;
+ select(undef, undef, undef, 0.05) if ($_is_WinEnv);
+
+ flock $_DAT_LOCK, LOCK_EX if ($_lock_chn);
print $_DAT_W_SOCK OUTPUT_W_EXT . $LF . $_chn . $LF;
print $_DAU_W_SOCK
@@ -1505,13 +1502,19 @@ sub exit {
$_exit_status . $LF . $_exit_id . $LF . $_len . $LF . $_exit_msg
;
- flock $_DAT_LOCK, LOCK_UN;
+ flock $_DAT_LOCK, LOCK_UN if ($_lock_chn);
+ flock $_DAE_LOCK, LOCK_UN;
+
+ close $_DAE_LOCK; undef $_DAE_LOCK;
}
## Exit thread/child process.
$SIG{__DIE__} = $SIG{__WARN__} = sub { };
- close $_DAT_LOCK; undef $_DAT_LOCK;
+ if ($_lock_chn) {
+ close $_DAT_LOCK; undef $_DAT_LOCK;
+ }
+
close $_COM_LOCK; undef $_COM_LOCK;
select STDERR; $| = 1;
diff --git a/lib/MCE.pod b/lib/MCE.pod
index 288dbd4..a5ee35c 100644
--- a/lib/MCE.pod
+++ b/lib/MCE.pod
@@ -5,7 +5,7 @@ MCE - Many-core Engine for Perl. Provides parallel processing capabilities.
=head1 VERSION
-This document describes MCE version 1.511
+This document describes MCE version 1.513
=head1 DESCRIPTION
diff --git a/lib/MCE/Core.pod b/lib/MCE/Core.pod
index ae4d9ba..1c05c99 100644
--- a/lib/MCE/Core.pod
+++ b/lib/MCE/Core.pod
@@ -5,7 +5,7 @@ MCE::Core - Documentation describing the core API for Many-core Engine
=head1 VERSION
-This document describes MCE::Core version 1.511
+This document describes MCE::Core version 1.513
=head1 SYNOPSIS
@@ -16,8 +16,8 @@ This is a simplistic use case of MCE running with 4 workers.
my $mce = MCE->new(
max_workers => 4,
user_func => sub {
- my ($self) = @_;
- print "Hello from ", $self->wid, "\n";
+ my ($mce) = @_;
+ print "Hello from ", $mce->wid, "\n";
}
);
@@ -47,7 +47,7 @@ of 0,1,2 makes it more legible when accessing the array elements.
use MCE CONST => 1; ## Same thing in 1.415 and later
user_func => sub {
- # my ($self, $chunk_ref, $chunk_id) = @_;
+ # my ($mce, $chunk_ref, $chunk_id) = @_;
print "Hello from ", $_[SELF]->wid, "\n";
}
@@ -56,7 +56,7 @@ of 0,1,2 makes it more legible when accessing the array elements.
use MCE;
user_func => sub {
- # my ($self, $chunk_ref, $chunk_id) = @_;
+ # my ($mce, $chunk_ref, $chunk_id) = @_;
print "Hello from ", MCE->wid, "\n";
}
@@ -188,7 +188,7 @@ Below, a new instance is configured with all available options.
## MCE release 1.4 adds a new parameter to allow one to
## specify arbitrary arguments such as a string, an ARRAY
## or a HASH reference. Workers can access this directly:
- ## my $args = $self->{user_args};
+ ## my $args = $mce->{user_args};
user_begin => \&user_begin, ## Default undef
user_func => \&user_func, ## Default undef
@@ -347,12 +347,12 @@ MCE->exit (children and threads), or die.
The format of $e->{pid} is PID_123 for children and THR_123 for threads.
sub on_post_exit {
- my ($self, $e) = @_;
+ my ($mce, $e) = @_;
print "$e->{wid}: $e->{pid}: $e->{status}: $e->{msg}: $e->{id}\n";
}
sub user_func {
- my $self = $_[0];
+ my $mce = $_[0];
MCE->exit(0, 'ok', 'pebbles');
}
@@ -377,14 +377,14 @@ called immediately whereas the latter is called after all workers have
completed processing or running.
sub on_post_run {
- my ($self, $status_ref) = @_;
+ my ($mce, $status_ref) = @_;
foreach my $e ( @{ $status_ref } ) {
print "$e->{wid}: $e->{pid}: $e->{status}: $e->{msg}: $e->{id}\n";
}
}
sub user_func {
- my $self = $_[0];
+ my $mce = $_[0];
MCE->exit(0, 'ok', 'pebbles');
}
@@ -434,7 +434,7 @@ The construction for user_func is as follow when chunk_size > 1 and assuming
use_slurpio equals 0.
user_func => sub {
- my ($self, $chunk_ref, $chunk_id) = @_;
+ my ($mce, $chunk_ref, $chunk_id) = @_;
## $_ is $chunk_ref->[0] when chunk_size equals 1
## $_ is $chunk_ref otherwise; $_ can be used below
@@ -477,7 +477,7 @@ reference for input_data.
# input_data => input_iterator(10, 30, 2),
user_func => sub {
- my ($self, $chunk_ref, $chunk_id) = @_;
+ my ($mce, $chunk_ref, $chunk_id) = @_;
MCE->print("$_: ", $_ * 2, "\n");
}
@@ -627,7 +627,7 @@ to have the process stop, otherwise will never end.
max_workers => 4, input_data => make_iterator(\@a),
user_func => sub {
- my ($self, $chunk_ref, $chunk_id) = @_;
+ my ($mce, $chunk_ref, $chunk_id) = @_;
MCE->print($_, "\n");
}
@@ -673,7 +673,7 @@ greater than $max.
input_data => make_iterator(\@a, 100),
user_func => sub {
- my ($self, $chunk_ref, $chunk_id) = @_;
+ my ($mce, $chunk_ref, $chunk_id) = @_;
MCE->print("$chunk_id: ", join(' ', @{ $chunk_ref }), "\n");
}
@@ -713,7 +713,7 @@ Sequence can be defined using an array or a hash reference.
},
user_func => sub {
- my ($self, $n, $chunk_id) = @_;
+ my ($mce, $n, $chunk_id) = @_;
print $n, " from ", MCE->wid(), " id ", $chunk_id, "\n";
}
);
@@ -800,19 +800,19 @@ worker during a run.
MCE 1.510 passes 2 additional parameters ($task_id and $task_name).
sub user_begin { ## Called once at the beginning
- my ($self, $task_id, $task_name) = @_;
- $self->{wk_total_rows} = 0;
+ my ($mce, $task_id, $task_name) = @_;
+ $mce->{wk_total_rows} = 0;
}
sub user_func { ## Called while processing
- my $self = shift;
- $self->{wk_total_rows} += 1;
+ my $mce = shift;
+ $mce->{wk_total_rows} += 1;
}
sub user_end { ## Called once at the end
- my ($self, $task_id, $task_name) = @_;
+ my ($mce, $task_id, $task_name) = @_;
printf "## %d: Processed %d rows\n",
- MCE->wid(), $self->{wk_total_rows};
+ MCE->wid(), $mce->{wk_total_rows};
}
my $mce = MCE->new(
@@ -832,10 +832,10 @@ e.g. $chunk_ref = [ record1, record2, record3, ... ]
sub user_func {
- my ($self, $chunk_ref, $chunk_id) = @_;
+ my ($mce, $chunk_ref, $chunk_id) = @_;
foreach my $row ( @{ $chunk_ref } ) {
- $self->{wk_total_rows} += 1;
+ $mce->{wk_total_rows} += 1;
print $row;
}
}
@@ -855,7 +855,7 @@ Here, a reference to a scalar containing the raw chunk data is processed.
sub user_func {
- my ($self, $chunk_ref, $chunk_id) = @_;
+ my ($mce, $chunk_ref, $chunk_id) = @_;
my $count = () = $$chunk_ref =~ /abc/;
}
@@ -887,7 +887,7 @@ Handy when wanting to filter, modify, and/or direct the output elsewhere.
}
sub user_func {
- my ($self, $chunk_ref, $chunk_id) = @_;
+ my ($mce, $chunk_ref, $chunk_id) = @_;
my $count = 0;
foreach my $row ( @{ $chunk_ref } ) {
@@ -937,7 +937,7 @@ processing.
input_data => $list_file,
task_end => sub {
- my ($self, $task_id, $task_name) = @_;
+ my ($mce, $task_id, $task_name) = @_;
print "Task [$task_id -- $task_name] completed processing\n";
},
@@ -971,7 +971,7 @@ user_func for input_data and sequence of numbers. The following applies.
$_ is a ref irregardless of whether chunk_size is 1 or greater
user_func => sub {
- # my ($self, $chunk_ref, $chunk_id) = @_;
+ # my ($mce, $chunk_ref, $chunk_id) = @_;
print ${ $_ }; ## $_ is same as $chunk_ref
}
@@ -981,7 +981,7 @@ user_func for input_data and sequence of numbers. The following applies.
$_ is same as $chunk_ref or $_[CHUNK]
user_func => sub {
- # my ($self, $chunk_ref, $chunk_id) = @_;
+ # my ($mce, $chunk_ref, $chunk_id) = @_;
for my $row ( @{ $_ } ) {
print $row, "\n";
}
@@ -990,7 +990,7 @@ user_func for input_data and sequence of numbers. The following applies.
use MCE CONST => 1;
user_func => sub {
- # my ($self, $chunk_ref, $chunk_id) = @_;
+ # my ($mce, $chunk_ref, $chunk_id) = @_;
for my $row ( @{ $_[CHUNK] } ) {
print $row, "\n";
}
@@ -1004,12 +1004,12 @@ user_func for input_data and sequence of numbers. The following applies.
## $chunk_ref is a reference to an array.
user_func => sub {
- # my ($self, $chunk_ref, $chunk_id) = @_;
+ # my ($mce, $chunk_ref, $chunk_id) = @_;
print $_, "\n; ## Same as $chunk_ref->[0];
}
MCE->foreach("/path/to/file", sub {
- # my ($self, $chunk_ref, $chunk_id) = @_;
+ # my ($mce, $chunk_ref, $chunk_id) = @_;
print $_; ## Same as $chunk_ref->[0];
});
@@ -1017,7 +1017,7 @@ user_func for input_data and sequence of numbers. The following applies.
## Both $_ and $n_seq are the same when chunk_size => 1.
MCE->forseq([ 1, 9 ], sub {
- # my ($self, $n_seq, $chunk_id) = @_;
+ # my ($mce, $n_seq, $chunk_id) = @_;
print $_, "\n"; ## Same as $n_seq
});
@@ -1031,7 +1031,7 @@ The code block receives an array containing the next 5 sequences. Chunk 1
same as $_, due to chunk_size being greater than 1.
MCE->forseq( [ 10, 40000, 2 ], { chunk_size => 5 }, sub {
- # my ($self, $n_seq, $chunk_id) = @_;
+ # my ($mce, $n_seq, $chunk_id) = @_;
my @result;
for my $n ( @{ $_ } ) {
... do work, append to result for 5
@@ -1057,7 +1057,7 @@ available worker. In essence, the abort method writes the last offset
position. Workers, on requesting the next offset position, will think
the end of input_data has been reached and leave the chunking loop.
- $self->abort();
+ $mce->abort();
MCE->abort();
=item ->chunk_id ( void )
@@ -1066,14 +1066,14 @@ Returns the chunk_id for the current chunk. The value starts at 1. Chunking
applies to workers processing input_data or sequence. The value is set to 0
for the manager process.
- my $chunk_id = $self->chunk_id();
+ my $chunk_id = $mce->chunk_id();
my $chunk_id = MCE->chunk_id();
=item ->chunk_size ( void )
Returns the chunk_size used by MCE.
- my $chunk_size = $self->chunk_size();
+ my $chunk_size = $mce->chunk_size();
my $chunk_size = MCE->chunk_size();
=item ->freeze ( $object_ref )
@@ -1082,14 +1082,14 @@ Calls the internal freeze method to serialize an object. The default
serialization routines are handled by Storable. Both freeze and thaw
can be overridden when including MCE.
- my $frozen = $self->freeze([ 0, 2, 4 ]);
+ my $frozen = $mce->freeze([ 0, 2, 4 ]);
my $frozen = MCE->freeze([ 0, 2, 4 ]);
=item ->max_workers ( void )
Returns the value for max_workers used by MCE.
- my $max_workers = $self->max_workers();
+ my $max_workers = $mce->max_workers();
my $max_workers = MCE->max_workers();
=item ->sess_dir ( void )
@@ -1097,14 +1097,14 @@ Returns the value for max_workers used by MCE.
Returns the session directory used by the MCE instance. This is defined
during spawning and removed during shutdown.
- my $sess_dir = $self->sess_dir();
+ my $sess_dir = $mce->sess_dir();
my $sess_dir = MCE->sess_dir();
=item ->task_id ( void )
Returns the task ID. This applies to the user_tasks option (starts at 0).
- my $task_id = $self->task_id();
+ my $task_id = $mce->task_id();
my $task_id = MCE->task_id();
=item ->task_name ( void )
@@ -1112,7 +1112,7 @@ Returns the task ID. This applies to the user_tasks option (starts at 0).
Returns the task_name value specified via the task_name option when
configuring MCE.
- my $task_name = $self->task_name();
+ my $task_name = $mce->task_name();
my $task_name = MCE->task_name();
=item ->task_wid ( void )
@@ -1121,28 +1121,28 @@ Returns the task worker ID (applies to user_tasks). The value starts at 1
per each task configured within user_tasks. The value is set to 0 for the
manager process.
- my $task_wid = $self->task_wid();
+ my $task_wid = $mce->task_wid();
my $task_wid = MCE->task_wid();
=item ->thaw ( $frozen )
Calls the internal thaw method to un-serialize the frozen object.
- my $object_ref = $self->thaw($frozen);
+ my $object_ref = $mce->thaw($frozen);
my $object_ref = MCE->thaw($frozen);
=item ->tmp_dir ( void )
Returns the temporary directory used by MCE.
- my $tmp_dir = $self->tmp_dir();
+ my $tmp_dir = $mce->tmp_dir();
my $tmp_dir = MCE->tmp_dir();
=item ->user_args ( void )
Returns the arguments specified via the user_args option.
- my ($arg1, $arg2, $arg3) = $self->user_args();
+ my ($arg1, $arg2, $arg3) = $mce->user_args();
my ($arg1, $arg2, $arg3) = MCE->user_args();
=item ->wid ( void )
@@ -1150,7 +1150,7 @@ Returns the arguments specified via the user_args option.
Returns the MCE worker ID. Starts at 1 per each MCE instance. The value is
set to 0 for the manager process.
- my $wid = $self->wid();
+ my $wid = $mce->wid();
my $wid = MCE->wid();
=back
@@ -1180,7 +1180,7 @@ process method.
## Arguments inside the code block are the same as for user_func.
MCE->forchunk(\@input_data, sub {
- my ($self, $chunk_ref, $chunk_id) = @_;
+ my ($mce, $chunk_ref, $chunk_id) = @_;
foreach ( @{ $chunk_ref } ) {
MCE->sendto("stdout", "$chunk_id: $_\n");
@@ -1205,7 +1205,7 @@ Arguments within the block are the same whether calling foreach or forchunk.
);
MCE->foreach(\@input_data, sub {
- my ($self, $chunk_ref, $chunk_id) = @_;
+ my ($mce, $chunk_ref, $chunk_id) = @_;
my $row = $chunk_ref->[0];
MCE->sendto("stdout", "$chunk_id: $row\n");
});
@@ -1226,7 +1226,7 @@ parallelize a serial loop with MCE.
## Parallel loop via MCE.
MCE->foreach([ (0 .. $max - 1) ], sub {
- my ($self, $chunk_ref, $chunk_id) = @_;
+ my ($mce, $chunk_ref, $chunk_id) = @_;
my $i = $chunk_ref->[0];
... ## Runs in parallel
});
@@ -1240,12 +1240,12 @@ Sequence can be defined using a hash or an array reference.
);
MCE->forseq({ begin => 15, end => 10, step => -1 }, sub {
- my ($self, $n, $chunk_id) = @_;
+ my ($mce, $n, $chunk_id) = @_;
print $n, " from ", MCE->wid(), "\n";
});
MCE->forseq([ 20, 40 ], sub {
- my ($self, $n, $chunk_id) = @_;
+ my ($mce, $n, $chunk_id) = @_;
my $result = `ping 192.168.1.${n}`;
...
});
@@ -1255,7 +1255,7 @@ sequences below. Chunking reduces IPC overhead behind the scene. Chunk size
is 1 when not specified.
MCE->forseq([ 20, 80 ], { chunk_size => 10 }, sub {
- my ($self, $n_seq, $chunk_id) = @_;
+ my ($mce, $n_seq, $chunk_id) = @_;
for my $n ( @{ $n_seq } ) {
my $result = `ping 192.168.1.${n}`;
...
@@ -1325,23 +1325,26 @@ Below is shown with overriding the default for max_workers.
MCE->shutdown();
-=item ->restart_worker ( $wid )
+=item ->restart_worker ( void )
One can restart a worker who has died or exited. The job never ends below due
-to restarting each time. The same wid from the worker, which has exited, is
-used (recommended).
+to restarting each time. Recommended is to call $mce->exit() or MCE->exit()
+instead of the native exit() function for better handling, especially under
+the Windows environment.
+
+The $e->{wid} argument is no longer necessary starting with the 1.5 release.
my $mce = MCE->new(
on_post_exit => sub {
- my ($self, $e) = @_;
+ my ($mce, $e) = @_;
print "$e->{wid}: $e->{pid}: status $e->{status}: $e->{msg}";
-
- MCE->restart_worker($e->{wid});
+ # MCE->restart_worker($e->{wid}); ## MCE-1.415 and below
+ MCE->restart_worker(); ## MCE-1.5 and above
},
user_begin => sub {
- my $self = $_[0];
+ my ($mce, $task_id, $task_name) = @_;
## Not interested in die messages going to STDERR.
## The die handler calls MCE->exit(255, $_[0]).
close STDERR;
@@ -1349,14 +1352,14 @@ used (recommended).
user_tasks => [{
max_workers => 5,
- user_func => sub {
- my $self = $_[0]; sleep MCE->wid();
+ user_func => sub {
+ my $mce = $_[0]; sleep MCE->wid();
MCE->exit(3, "exited from " . MCE->wid() . "\n");
}
},{
max_workers => 4,
- user_func => sub {
- my $self = $_[0]; sleep MCE->wid();
+ user_func => sub {
+ my $mce = $_[0]; sleep MCE->wid();
die("died from " . MCE->wid() . "\n");
}
}]
@@ -1410,11 +1413,11 @@ process method.
The send method is useful when wanting to spawn workers early to minimize
memory consumption and afterwards send data individually to each worker. One
cannot send more than the total workers spawned. Workers store the received
-data as $self->{user_data}.
+data as $mce->{user_data}.
The data which can be sent is restricted to an ARRAY, HASH, or PDL reference.
Workers begin processing immediately after receiving data. Workers set
-$self->{user_data} to undef after processing. One cannot specify input_data,
+$mce->{user_data} to undef after processing. One cannot specify input_data,
sequence, or user_tasks for the MCE instance to receive user data via the
"send" method.
@@ -1425,8 +1428,8 @@ immediately after receiving user data.
max_workers => 5,
user_func => sub {
- my ($self) = @_;
- my $data = $self->{user_data};
+ my ($mce) = @_;
+ my $data = $mce->{user_data};
my $first_name = $data->{first_name};
print MCE->wid, ": Hello from $first_name\n";
}
@@ -1606,7 +1609,7 @@ foreach, forchunk, forseq, and user_func.
MCE->forchunk(\@list, { chunk_size => 2 }, sub {
- my ($self, $chunk_ref, $chunk_id) = @_;
+ my ($mce, $chunk_ref, $chunk_id) = @_;
MCE->last if ($chunk_id > 4);
my @output = ();
@@ -1638,7 +1641,7 @@ foreach, forchunk, forseq, and user_func.
MCE->forchunk(\@list, { chunk_size => 4 }, sub {
- my ($self, $chunk_ref, $chunk_id) = @_;
+ my ($mce, $chunk_ref, $chunk_id) = @_;
MCE->next if ($chunk_id < 20);
my @output = ();
@@ -1743,7 +1746,7 @@ Barrier synchronization (sync) was added to MCE 1.406.
sub user_func {
- my ($self) = @_;
+ my ($mce) = @_;
my $wid = MCE->wid();
MCE->sendto('stdout', "a: $wid\n");
@@ -1804,7 +1807,7 @@ or MCE->sendto methods inside a loop.
sub user_func {
- my ($self) = @_;
+ my ($mce) = @_;
my @result;
for (1 .. 3) {
@@ -1886,7 +1889,7 @@ The user_tasks is configured to simulate 4 nodes below. The demonstration uses
sub user_begin {
- my ($self) = @_;
+ my ($mce, $task_id, $task_name) = @_;
## The yield method causes this worker to wait for its next
## time interval slot before running. Yield has no effect
@@ -1911,7 +1914,7 @@ The user_tasks is configured to simulate 4 nodes below. The demonstration uses
sub user_func {
- my ($self, $seq_n, $chunk_id) = @_;
+ my ($mce, $seq_n, $chunk_id) = @_;
## Yield simply waits for the next time interval.
MCE->yield;
diff --git a/lib/MCE/Core/Input/Generator.pm b/lib/MCE/Core/Input/Generator.pm
index 1fbd0d2..edaa480 100644
--- a/lib/MCE/Core/Input/Generator.pm
+++ b/lib/MCE/Core/Input/Generator.pm
@@ -12,7 +12,7 @@
package MCE::Core::Input::Generator;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
## Items below are folded into MCE.
diff --git a/lib/MCE/Core/Input/Handle.pm b/lib/MCE/Core/Input/Handle.pm
index 645b6d4..290af4b 100644
--- a/lib/MCE/Core/Input/Handle.pm
+++ b/lib/MCE/Core/Input/Handle.pm
@@ -11,7 +11,7 @@
package MCE::Core::Input::Handle;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
## Items below are folded into MCE.
diff --git a/lib/MCE/Core/Input/Iterator.pm b/lib/MCE/Core/Input/Iterator.pm
index d8e97bb..d5edf82 100644
--- a/lib/MCE/Core/Input/Iterator.pm
+++ b/lib/MCE/Core/Input/Iterator.pm
@@ -11,7 +11,7 @@
package MCE::Core::Input::Iterator;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
## Items below are folded into MCE.
diff --git a/lib/MCE/Core/Input/Request.pm b/lib/MCE/Core/Input/Request.pm
index dfdd5b1..45bfadc 100644
--- a/lib/MCE/Core/Input/Request.pm
+++ b/lib/MCE/Core/Input/Request.pm
@@ -11,7 +11,7 @@
package MCE::Core::Input::Request;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
## Items below are folded into MCE.
diff --git a/lib/MCE/Core/Input/Sequence.pm b/lib/MCE/Core/Input/Sequence.pm
index 1d0a081..9bf0fe2 100644
--- a/lib/MCE/Core/Input/Sequence.pm
+++ b/lib/MCE/Core/Input/Sequence.pm
@@ -11,7 +11,7 @@
package MCE::Core::Input::Sequence;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
## Items below are folded into MCE.
diff --git a/lib/MCE/Core/Manager.pm b/lib/MCE/Core/Manager.pm
index ec85f7d..3abb790 100644
--- a/lib/MCE/Core/Manager.pm
+++ b/lib/MCE/Core/Manager.pm
@@ -11,7 +11,7 @@
package MCE::Core::Manager;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
## Items below are folded into MCE.
diff --git a/lib/MCE/Core/Validation.pm b/lib/MCE/Core/Validation.pm
index f661f86..3b0ef7f 100644
--- a/lib/MCE/Core/Validation.pm
+++ b/lib/MCE/Core/Validation.pm
@@ -11,7 +11,7 @@
package MCE::Core::Validation;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
## Items below are folded into MCE.
diff --git a/lib/MCE/Core/Worker.pm b/lib/MCE/Core/Worker.pm
index e026ffb..aecdd78 100644
--- a/lib/MCE/Core/Worker.pm
+++ b/lib/MCE/Core/Worker.pm
@@ -11,7 +11,7 @@
package MCE::Core::Worker;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
## Items below are folded into MCE.
diff --git a/lib/MCE/Examples.pod b/lib/MCE/Examples.pod
index e26932a..9eec9ab 100644
--- a/lib/MCE/Examples.pod
+++ b/lib/MCE/Examples.pod
@@ -5,7 +5,7 @@ MCE::Examples - A list of examples demonstrating Many-core Engine
=head1 VERSION
-This document describes MCE::Examples version 1.511
+This document describes MCE::Examples version 1.513
=head1 DESCRIPTION
@@ -69,6 +69,10 @@ count aggregation.
divide-and-conquer algorithm. Also included are 2 plain
Perl examples.
+ pipe1.pl, pipe2.pl
+ Process STDIN or FILE in parallel. Processing is via Perl
+ for pipe1.pl, whereas an external command for pipe2.pl.
+
scaling_pings.pl
Perform ping test and report back failed IPs to standard
output.
diff --git a/lib/MCE/Flow.pm b/lib/MCE/Flow.pm
index 31b1155..4ad3da3 100644
--- a/lib/MCE/Flow.pm
+++ b/lib/MCE/Flow.pm
@@ -14,7 +14,7 @@ use Scalar::Util qw( looks_like_number );
use MCE;
use MCE::Util;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
###############################################################################
## ----------------------------------------------------------------------------
@@ -432,7 +432,7 @@ MCE::Flow - Parallel flow model for building creative applications
=head1 VERSION
-This document describes MCE::Flow version 1.511
+This document describes MCE::Flow version 1.513
=head1 DESCRIPTION
diff --git a/lib/MCE/Grep.pm b/lib/MCE/Grep.pm
index 3b3d08f..8340c08 100644
--- a/lib/MCE/Grep.pm
+++ b/lib/MCE/Grep.pm
@@ -14,7 +14,7 @@ use Scalar::Util qw( looks_like_number );
use MCE;
use MCE::Util;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
###############################################################################
## ----------------------------------------------------------------------------
@@ -398,7 +398,7 @@ MCE::Grep - Parallel grep model similar to the native grep function
=head1 VERSION
-This document describes MCE::Grep version 1.511
+This document describes MCE::Grep version 1.513
=head1 SYNOPSIS
diff --git a/lib/MCE/Loop.pm b/lib/MCE/Loop.pm
index bbc825c..488d299 100644
--- a/lib/MCE/Loop.pm
+++ b/lib/MCE/Loop.pm
@@ -14,7 +14,7 @@ use Scalar::Util qw( looks_like_number );
use MCE;
use MCE::Util;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
###############################################################################
## ----------------------------------------------------------------------------
@@ -325,7 +325,7 @@ MCE::Loop - Parallel loop model for building creative loops
=head1 VERSION
-This document describes MCE::Loop version 1.511
+This document describes MCE::Loop version 1.513
=head1 DESCRIPTION
diff --git a/lib/MCE/Map.pm b/lib/MCE/Map.pm
index c8fa448..e2beeed 100644
--- a/lib/MCE/Map.pm
+++ b/lib/MCE/Map.pm
@@ -14,7 +14,7 @@ use Scalar::Util qw( looks_like_number );
use MCE;
use MCE::Util;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
###############################################################################
## ----------------------------------------------------------------------------
@@ -398,7 +398,7 @@ MCE::Map - Parallel map model similar to the native map function
=head1 VERSION
-This document describes MCE::Map version 1.511
+This document describes MCE::Map version 1.513
=head1 SYNOPSIS
diff --git a/lib/MCE/Queue.pm b/lib/MCE/Queue.pm
index f635a85..69831a7 100644
--- a/lib/MCE/Queue.pm
+++ b/lib/MCE/Queue.pm
@@ -14,7 +14,7 @@ use Socket qw( :crlf PF_UNIX PF_UNSPEC SOCK_STREAM );
use Scalar::Util qw( looks_like_number );
use bytes;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
###############################################################################
## ----------------------------------------------------------------------------
@@ -1600,7 +1600,7 @@ MCE::Queue - Hybrid queues (normal including priority) for Many-core Engine
=head1 VERSION
-This document describes MCE::Queue version 1.511
+This document describes MCE::Queue version 1.513
=head1 SYNOPSIS
diff --git a/lib/MCE/Signal.pm b/lib/MCE/Signal.pm
index cbb931c..3cc0156 100644
--- a/lib/MCE/Signal.pm
+++ b/lib/MCE/Signal.pm
@@ -12,7 +12,7 @@ use warnings;
use Fcntl qw( :flock O_RDONLY );
use base qw( Exporter );
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
our ($has_threads, $main_proc_id, $prog_name);
our ($display_die_with_localtime, $display_warn_with_localtime);
@@ -43,6 +43,7 @@ sub _croak { require Carp; goto &Carp::croak; }
sub _usage { _croak "MCE::Signal error: $_[0] is not a valid option"; }
sub _flag { 1; }
+my $_is_MSWin32 = ($^O eq 'MSWin32');
my $_keep_tmp_dir = 0;
my $_no_sigmsg = 0;
my $_no_kill9 = 0;
@@ -81,12 +82,12 @@ sub import {
# setpgrp(0,0) if ($_no_setpgrp == 0 && $^O ne 'MSWin32');
## Sets the current process group for the current process.
- setpgrp(0,0) if ($_setpgrp == 1 && $^O ne 'MSWin32');
+ setpgrp($main_proc_id, 0) if ($_setpgrp == 1 && $^O ne 'MSWin32');
my ($_tmp_dir_base, $_count);
if (exists $ENV{TEMP}) {
- if ($^O eq 'MSWin32') {
+ if ($_is_MSWin32) {
$_tmp_dir_base = $ENV{TEMP} . '/mce';
mkdir $_tmp_dir_base unless (-d $_tmp_dir_base);
}
@@ -132,7 +133,7 @@ $SIG{TERM} = \&stop_and_exit; ## UNIX SIG 15
## the reaping of it's children, especially when running multiple MCEs
## simultaneously.
##
-$SIG{CHLD} = 'DEFAULT' if ($^O ne 'MSWin32');
+$SIG{CHLD} = 'DEFAULT' unless ($_is_MSWin32);
###############################################################################
## ----------------------------------------------------------------------------
@@ -167,15 +168,19 @@ END {
sub sys_cmd {
shift @_ if (defined $_[0] && $_[0] eq 'MCE::Signal');
- _croak("MCE::Signal::sys_cmd: no arguments was specified") if (@_ == 0);
+ _croak("MCE::Signal::sys_cmd: no arguments were specified") if (@_ == 0);
my $_status = system(@_);
my $_sig_no = $_status & 127;
my $_exit_status = $_status >> 8;
- ## Kill this process if command caught SIGINT or SIGQUIT.
- kill('INT', $$) if $_sig_no == 2;
- kill('QUIT', $$) if $_sig_no == 3;
+ ## Kill the process group if command caught SIGINT or SIGQUIT.
+
+ kill('INT', $main_proc_id, ($_is_MSWin32 ? -$$ : -getpgrp()))
+ if $_sig_no == 2;
+
+ kill('QUIT', $main_proc_id, ($_is_MSWin32 ? -$$ : -getpgrp()))
+ if $_sig_no == 3;
return $_exit_status;
}
@@ -218,12 +223,10 @@ sub sys_cmd {
## ----------------------------------------------------------------------
- ## For main thread / parent process.
+ ## For the main thread / manager process.
if ($$ == $main_proc_id) {
- $_handler_cnt += 1;
-
- if ($_handler_cnt == 1 && ! -e "$tmp_dir/stopped") {
+ if (++$_handler_cnt == 1 && ! -e "$tmp_dir/stopped") {
open my $_FH, "> $tmp_dir/stopped"; close $_FH;
local $\ = undef;
@@ -255,11 +258,13 @@ sub sys_cmd {
open my $_FH, "> $tmp_dir/killed"; close $_FH;
## Signal process group to terminate.
- kill('TERM', -$$);
+ kill('TERM', $_is_MSWin32 ? -$$ : -getpgrp());
## Pause a bit.
if ($_sig_name ne 'PIPE') {
select(undef, undef, undef, 0.066) for (1..3);
+ } else {
+ select(undef, undef, undef, 0.011) for (1..2);
}
}
@@ -287,10 +292,9 @@ sub sys_cmd {
## Signal process group to die.
if ($_is_sig == 1) {
- print STDERR "\n"
- if ($_sig_name ne 'PIPE' && $_no_sigmsg == 0);
+ print STDERR "\n" if ($_sig_name ne 'PIPE' && $_no_sigmsg == 0);
- kill('KILL', -$$, $main_proc_id)
+ kill('KILL', ($_is_MSWin32 ? -$$ : -getpgrp()), $main_proc_id)
if ($_sig_name eq 'PIPE' || $_no_kill9 == 0);
}
}
@@ -301,30 +305,32 @@ sub sys_cmd {
## For child processes.
if ($$ != $main_proc_id && $_is_sig == 1 && -d $tmp_dir) {
- ## Obtain lock.
- open my $CHILD_LOCK, '+>>', "$tmp_dir/child.lock";
- flock $CHILD_LOCK, LOCK_EX;
-
- $_handler_cnt += 1;
-
## Signal process group to terminate.
- if ($_handler_cnt == 1) {
+ if (++$_handler_cnt == 1) {
+
+ ## Obtain lock.
+ open my $CHILD_LOCK, '+>>', "$tmp_dir/child.lock";
+ flock $CHILD_LOCK, LOCK_EX;
## Notify the main process that I've died.
if ($_sig_name eq '__DIE__' && ! -f "$tmp_dir/died") {
- open my $_FH, "> $tmp_dir/died"; close $_FH;
+ local $@; eval '
+ open my $_FH, "> $tmp_dir/died"; close $_FH;
+ ';
}
## Signal process group to terminate.
if (! -f "$tmp_dir/killed" && ! -f "$tmp_dir/stopped") {
- open my $_FH, "> $tmp_dir/killed"; close $_FH;
- kill('TERM', -$$, $main_proc_id);
+ local $@; eval '
+ open my $_FH, "> $tmp_dir/killed"; close $_FH;
+ ';
+ kill('TERM', $main_proc_id, -$$);
}
- }
- ## Release lock.
- flock $CHILD_LOCK, LOCK_UN;
- close $CHILD_LOCK;
+ ## Release lock.
+ flock $CHILD_LOCK, LOCK_UN;
+ close $CHILD_LOCK;
+ }
}
## ----------------------------------------------------------------------
@@ -440,7 +446,7 @@ MCE::Signal - Temporary directory creation/cleanup & signal handling
=head1 VERSION
-This document describes MCE::Signal version 1.511
+This document describes MCE::Signal version 1.513
=head1 SYNOPSIS
@@ -466,9 +472,10 @@ Windows.
As of MCE 1.405, MCE::Signal no longer calls setpgrp by default. Pass the
-setpgrp option to MCE::Signal to call setpgrp.
- ## Running MCE through Daemon::Control requires setpgrp to be called.
+ ## Running MCE through Daemon::Control requires setpgrp to be called
+ ## for MCE releases 1.511 and below.
- use MCE::Signal qw(-setpgrp);
+ use MCE::Signal qw(-setpgrp); ## Not necessary for MCE 1.512 and above
use MCE;
The following are available arguments and their meanings.
@@ -483,11 +490,13 @@ The following are available arguments and their meanings.
-setpgrp - Calls setpgrp to set the process group for the process
- Specify this option to ensure all workers terminate
- when reading STDIN like so:
+ This option ensures all workers terminate when reading
+ STDIN for MCE releases 1.511 and below.
+
cat big_input_file | ./mce_script.pl | head -10
This works fine without the -setpgrp option:
+
./mce_script.pl < big_input_file | head -10
Nothing is exported by default. Exportable are 1 variable and 2 subroutines.
@@ -507,10 +516,17 @@ Nothing is exported by default. Exportable are 1 variable and 2 subroutines.
=head2 sys_cmd ( $command )
- ## Execute command and return the actual exit status. The calling
- ## process is also signaled if command caught SIGINT or SIGQUIT.
+The system function in Perl ignores SIGNINT and SIGQUIT. These 2 signals are
+sent to the command being executed via system() but not back to the underlying
+Perl script. For this reason, sys_cmd was added to MCE::Signal.
+
+ ## Execute command and return the actual exit status. The perl script
+ ## is also signaled if command caught SIGINT or SIGQUIT.
+
+ use MCE::Signal qw(sys_cmd); ## Include before MCE
+ use MCE;
- my $exit_status = MCE::Signal::sys_cmd($command);
+ my $exit_status = sys_cmd($command);
=head1 EXAMPLES
diff --git a/lib/MCE/Step.pm b/lib/MCE/Step.pm
index d4e4a05..a167f9c 100644
--- a/lib/MCE/Step.pm
+++ b/lib/MCE/Step.pm
@@ -16,7 +16,7 @@ use MCE::Util;
use MCE::Queue;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
###############################################################################
## ----------------------------------------------------------------------------
@@ -515,7 +515,7 @@ MCE::Step - Parallel step model for building creative steps
=head1 VERSION
-This document describes MCE::Step version 1.511
+This document describes MCE::Step version 1.513
=head1 DESCRIPTION
diff --git a/lib/MCE/Stream.pm b/lib/MCE/Stream.pm
index 9b79d48..bd6a4fe 100644
--- a/lib/MCE/Stream.pm
+++ b/lib/MCE/Stream.pm
@@ -16,7 +16,7 @@ use MCE::Util;
use MCE::Queue;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
###############################################################################
## ----------------------------------------------------------------------------
@@ -609,7 +609,7 @@ MCE::Stream - Parallel stream model for chaining multiple maps and greps
=head1 VERSION
-This document describes MCE::Stream version 1.511
+This document describes MCE::Stream version 1.513
=head1 SYNOPSIS
diff --git a/lib/MCE/Subs.pm b/lib/MCE/Subs.pm
index 10f7fc4..59006e3 100644
--- a/lib/MCE/Subs.pm
+++ b/lib/MCE/Subs.pm
@@ -11,7 +11,7 @@ use warnings;
use MCE;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
###############################################################################
## ----------------------------------------------------------------------------
@@ -193,7 +193,7 @@ MCE::Subs - Exports functions mapped directly to MCE methods
=head1 VERSION
-This document describes MCE::Subs version 1.511
+This document describes MCE::Subs version 1.513
=head1 SYNOPSIS
diff --git a/lib/MCE/Util.pm b/lib/MCE/Util.pm
index 677ec95..fc7517c 100644
--- a/lib/MCE/Util.pm
+++ b/lib/MCE/Util.pm
@@ -12,7 +12,7 @@ use warnings;
use base qw( Exporter );
use bytes;
-our $VERSION = '1.511'; $VERSION = eval $VERSION;
+our $VERSION = '1.513'; $VERSION = eval $VERSION;
our @EXPORT_OK = qw( get_ncpu );
our %EXPORT_TAGS = ( all => \@EXPORT_OK );
@@ -228,7 +228,7 @@ MCE::Util - Public and private utility functions for Many-core Engine
=head1 VERSION
-This document describes MCE::Util version 1.511
+This document describes MCE::Util version 1.513
=head1 SYNOPSIS