MCE
view release on metacpan or search on metacpan
###############################################################################
## ----------------------------------------------------------------------------
## MCE - Many-Core Engine for Perl providing parallel processing capabilities.
##
###############################################################################
package MCE;
use strict;
use warnings;
no warnings qw( threads recursion uninitialized );
our $VERSION = '1.904';
## no critic (BuiltinFunctions::ProhibitStringyEval)
## no critic (Subroutines::ProhibitSubroutinePrototypes)
## no critic (TestingAndDebugging::ProhibitNoStrict)
use Carp ();
my ($_has_threads, $_freeze, $_thaw, $_tid, $_oid);
BEGIN {
local $@;
if ( $^O eq 'MSWin32' && ! $INC{'threads.pm'} ) {
eval 'use threads; use threads::shared;';
}
elsif ( $INC{'threads.pm'} && ! $INC{'threads/shared.pm'} ) {
eval 'use threads::shared;';
}
$_has_threads = $INC{'threads.pm'} ? 1 : 0;
$_tid = $_has_threads ? threads->tid() : 0;
$_oid = "$$.$_tid";
if ( $] ge '5.008008' && ! $INC{'PDL.pm'} ) {
eval 'use Sereal::Encoder 3.015; use Sereal::Decoder 3.015;';
if ( ! $@ ) {
my $_encoder_ver = int( Sereal::Encoder->VERSION() );
my $_decoder_ver = int( Sereal::Decoder->VERSION() );
if ( $_encoder_ver - $_decoder_ver == 0 ) {
$_freeze = \&Sereal::Encoder::encode_sereal;
$_thaw = \&Sereal::Decoder::decode_sereal;
}
}
}
if ( ! defined $_freeze ) {
require Storable;
$_freeze = \&Storable::freeze;
$_thaw = \&Storable::thaw;
}
}
use IO::Handle ();
use Scalar::Util qw( looks_like_number refaddr reftype weaken );
use Socket qw( SOL_SOCKET SO_RCVBUF );
use Time::HiRes qw( sleep time );
use MCE::Util qw( $LF );
use MCE::Signal ();
use MCE::Mutex ();
our ($MCE, $RLA, $_que_template, $_que_read_size);
our (%_valid_fields_new);
my ($TOP_HDLR, $_is_MSWin32, $_is_winenv, $_prev_mce);
my (%_valid_fields_task, %_params_allowed_args);
BEGIN {
## Configure pack/unpack template for writing to and from the queue.
## Each entry contains 2 positive numbers: chunk_id & msg_id.
## Check for >= 64-bit, otherwize fall back to machine's word length.
$_que_template = ( ( log(~0+1) / log(2) ) >= 64 ) ? 'Q2' : 'I2';
$_que_read_size = length pack($_que_template, 0, 0);
## Attributes used internally.
## _abort_msg _caller _chn _com_lock _dat_lock _mgr_live _rla_data _seed
## _chunk_id _pids _run_mode _single_dim _thrs _tids _task_wid _wid _wuf
## _exiting _exit_pid _last_sref _total_exited _total_running _total_workers
## _send_cnt _sess_dir _spawned _state _status _task _task_id _wrk_status
## _init_pid _init_total_workers _pids_t _pids_w _pids_c _relayed
##
## _bsb_r_sock _bsb_w_sock _com_r_sock _com_w_sock _dat_r_sock _dat_w_sock
## _que_r_sock _que_w_sock _rla_r_sock _rla_w_sock _data_channels
## _lock_chn _mutex_n
%_valid_fields_new = map { $_ => 1 } qw(
max_workers tmp_dir use_threads user_tasks task_end task_name freeze thaw
chunk_size input_data sequence job_delay spawn_delay submit_delay RS
flush_file flush_stderr flush_stdout stderr_file stdout_file use_slurpio
interval user_args user_begin user_end user_func user_error user_output
bounds_only gather init_relay on_post_exit on_post_run parallel_io
loop_timeout max_retries progress posix_exit
);
}
for my $_p (qw( user_begin user_func user_end )) {
if (defined $_params_ref->{$_p}) {
$self->{$_p} = delete $_params_ref->{$_p};
$_requires_shutdown = 1;
}
}
for my $_p (keys %{ $_params_ref }) {
_croak("MCE::_sync_params: ($_p) is not a valid params argument")
unless (exists $_params_allowed_args{$_p});
$self->{$_p} = $_params_ref->{$_p};
}
return ($self->{_spawned}) ? $_requires_shutdown : 0;
}
###############################################################################
## ----------------------------------------------------------------------------
## Dispatch methods.
##
###############################################################################
sub _dispatch {
my @_args = @_; my $_is_thread = shift @_args;
my $self = $MCE = $_args[0];
## To avoid (Scalars leaked: N) messages; fixed in Perl 5.12.x
@_ = ();
$ENV{'PERL_MCE_IPC'} = 'win32' if ( $_is_MSWin32 && (
defined($self->{max_retries}) ||
$INC{'MCE/Child.pm'} ||
$INC{'MCE/Hobo.pm'}
));
delete $self->{_relayed};
$self->{_is_thread} = $_is_thread;
$self->{_pid} = $_is_thread ? $$ .'.'. threads->tid() : $$;
if (!$self->{use_threads}) {
MCE::Child->_clear() if $INC{'MCE/Child.pm'};
MCE::Hobo->_clear() if $INC{'MCE/Hobo.pm'};
}
# Set the seed of the base generator uniquely between workers.
# The new seed is computed using the current seed and ID value.
# One may set the seed at the application level for predictable
# results (non-thread workers only). Ditto for Math::Prime::Util,
# Math::Random, Math::Random::MT::Auto, and PDL.
#
# MCE 1.892, 2024-06-08: Enable predictability running threads.
# Output matches non-threads for CORE, Math::Prime::Util, and
# Math::Random::MT::Auto. https://perlmonks.org/?node_id=11159834
{
my $_wid = $_args[1];
my $_seed = abs($self->{_seed} - ($_wid * 100000)) % 2147483560;
CORE::srand($_seed) if (!$self->{use_threads} || $] ge '5.020000'); # drand48
Math::Prime::Util::srand($_seed) if $INC{'Math/Prime/Util.pm'};
# [etj] identified a race condition in PDL running threads
# https://perlmonks.org/?node_id=11159841
if (!$self->{use_threads}) {
PDL::srand($_seed) if $INC{'PDL.pm'} && PDL->can('srand'); # PDL 2.062 ~ 2.089
PDL::srandom($_seed) if $INC{'PDL.pm'} && PDL->can('srandom'); # PDL 2.089_01+
}
}
if (!$self->{use_threads} && $INC{'Math/Random.pm'}) {
my ($_wid, $_cur_seed) = ($_args[1], Math::Random::random_get_seed());
my $_new_seed = ($_cur_seed < 1073741781)
? $_cur_seed + (($_wid * 100000) % 1073741780)
: $_cur_seed - (($_wid * 100000) % 1073741780);
Math::Random::random_set_seed($_new_seed, $_new_seed);
}
if ($INC{'Math/Random/MT/Auto.pm'}) {
my ($_wid, $_cur_seed) = (
$_args[1], Math::Random::MT::Auto::get_seed()->[0]
);
my $_new_seed = ($_cur_seed < 1073741781)
? $_cur_seed + (($_wid * 100000) % 1073741780)
: $_cur_seed - (($_wid * 100000) % 1073741780);
Math::Random::MT::Auto::set_seed($_new_seed);
}
## Run.
_worker_main(@_args, \@_plugin_worker_init);
_exit($self);
}
sub _dispatch_thread {
my ($self, $_wid, $_task, $_task_id, $_task_wid, $_params) = @_;
@_ = (); local $_;
my $_thr = threads->create( \&_dispatch,
1, $self, $_wid, $_task, $_task_id, $_task_wid, $_params
);
_croak("MCE::_dispatch_thread: Failed to spawn worker $_wid: $!")
if (!defined $_thr);
## Store into an available slot (restart), otherwise append to arrays.
if (defined $_params) { for my $_i (0 .. @{ $self->{_tids} } - 1) {
unless (defined $self->{_tids}->[$_i]) {
$self->{_thrs}->[$_i] = $_thr;
$self->{_tids}->[$_i] = $_thr->tid();
return;
}
}}
push @{ $self->{_thrs} }, $_thr;
( run in 0.869 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )