Async-Simple-Pool
view release on metacpan or search on metacpan
lib/Async/Simple/Task/ForkTmpFile.pm view on Meta::CPAN
my ( $data ) = @_; # source data for task
... your task code ...
return( $result );
}
=cut
=head2 answer
Result of current task
=cut
=head2 has_answer
has_answer is true, if the task has been finished and result has been ready
=cut
=head2 timeout
timeout - positive numeric value = seconds between checking for result.
inherited from Async::Simple::Task.
=cut
=head2 kill_on_exit
Kills process from parent in case of object desctuction
=cut
=head2 new()
my $task = Async::Simple::Task::ForkTmpFile->new( %all_optional_params );
Possible keys for %all_optional_params:
task => coderef, function, called for each "data" passed to child process via $task->put( $data );
timeout => timeout in seconds between child checkings for new data passed. default 0.01
kill_on_exit => kill (1) or not (0) subprocess on object destroy (1 by default).
=cut
=head2 tmp_dir
Path, that used for store tomporary files.
This path must be writable.
It can be empty; in this case ( File::Spec->tmpdir() || $ENV{TEMP} ) will be used
By default:
During taint -T mode always writes files to current directory ( path = '' )
Windows outside taint -T mode writes files by default to C:\TEMP or C:\TMP
Unix outside taint -T mode writes files by default to /var/tmp/
=cut
has tmp_dir => (
is => 'ro',
isa => 'Str',
lazy => 1,
builder => 'make_tmp_dir',
);
sub make_tmp_dir {
my ( $self ) = @_;
my $tmp_dir = File::Spec->tmpdir() || '';
# For WIN taint mode calculated path starts with '\'. Remove it and stay at current(empty) dir
$tmp_dir = '' if $tmp_dir =~ /^\\$/;
# TEMP = C:\Users\XXXXXX~1\AppData\Local\Temp
$tmp_dir ||= $ENV{TEMP} // '';
# Untaint ENV: fallback, if File::Spec->tmpdir failed
return [ $tmp_dir =~ /^(.+)$/ ]->[0];
};
=head2 BUILD
internal. Some tricks here:)
1. Master process called $task->new with fork() inside
2. After forking done we have two processes:
2.1. Master gets one side of reader/writer tmp file handlers and pid of child
2.2. Child - another side of tmp file handlers and extra logic with everlasting loop
=cut
# Writable pipe between parent and child.
# Each of them has pair of handlers, for duplex communication.
has writer => (
is => 'rw',
isa => 'Str',
);
# Readable pipe between parent and child.
# Each of them has pair of handlers, for duplex communication.
has reader => (
is => 'rw',
isa => 'Str',
);
=head2 fork_child
Makes child process and returns pid of child process to parent or 0 to child process
=cut
sub fork_child {
my ( $self ) = @_;
my( $randname, $parent_writer_fname, $parent_reader_fname );
$randname = sub {
my @x = ( 'a'..'z', 'A'..'Z', 0..9 );
join( "", map { $x[ int( rand @x - 0.01 ) ] } 1 .. 64 )
};
for ( 1..10 ) {
$parent_writer_fname = File::Spec->catfile( $self->tmp_dir, '_pw_tmp_' . $randname->() );
$parent_reader_fname = File::Spec->catfile( $self->tmp_dir, '_pr_tmp_' . $randname->() );
next if -f $parent_writer_fname || -f $parent_reader_fname;
last;
};
die 'Can`t obtain unique fname' if -f $parent_writer_fname || -f $parent_reader_fname;
my $pid = fork() // die "fork() failed: $!";
# With taint mode we use current directory as temp,
# Otherwise - default writable temp directory from File::Spec->tmpdir();
# child
unless ( $pid ) {
$self->writer( $parent_reader_fname );
$self->reader( $parent_writer_fname );
# Important!
# Just after that we trap into BUILD
# with the infinitive loop for child process (pid=0)
return 0;
}
# parent
$self->writer( $parent_writer_fname );
$self->reader( $parent_reader_fname );
return $pid;
};
=head2 get
Reads from task, if something can be readed or returns undef after timeout.
my $result = $task->get;
Please note! If your function can return an undef value, then you shoud check
$task->has_result.
=cut
sub get {
my ( $self ) = @_;
# Try to read "marker" into data within timeout
# Each pack starts with an empty line and serialized string of useful data.
open( my $fh, '<', $self->reader ) or return;
my $data = <$fh>;
return unless defined $data;
return unless $data =~ /\n/;
close( $fh );
# In case, when reader still opened for writing
# We are not allowed to remove file, so we should wait
for ( 1..10 ) {
last if unlink $self->reader;
sleep $self->timeout;
}
my $answer = $data
? eval{ $self->serializer->deserialize( $data )->[0] }
: undef;
$self->answer( $answer );
};
( run in 0.524 second using v1.01-cache-2.11-cpan-13bb782fe5a )