Convert-Pheno

 view release on metacpan or  search on metacpan

lib/Convert/Pheno/HTTP/Jobs.pm  view on Meta::CPAN

package Convert::Pheno::HTTP::Jobs;

use strict;
use warnings;
use Cwd qw(abs_path);
use Digest::SHA qw(sha256_hex);
use Encode qw(decode FB_DEFAULT);
use File::Copy qw(copy);
use File::Find qw(find);
use File::Path qw(make_path remove_tree);
use File::Spec;
use Fcntl qw(LOCK_EX LOCK_SH LOCK_NB);
use IPC::Open3 qw(open3);
use JSON::XS;
use Mojo::IOLoop;
use Mojo::Util qw(sha1_sum);
use Path::Tiny qw(path);
use POSIX qw(WNOHANG);
use Symbol qw(gensym);
use Convert::Pheno::HTTP::Service qw(execute execute_files catalog);
use Convert::Pheno::IO::Atomic qw(write_atomically);

my $JSON = JSON::XS->new->utf8->canonical->pretty;
my $MAX_CONCURRENT_JOBS = 16;

sub _validate_job_limit {
    my ($value, $maximum) = @_;
    $maximum //= $MAX_CONCURRENT_JOBS;
    die "Maximum concurrent jobs must be an integer from 1 to $maximum\n"
      unless defined($value) && !ref($value) && "$value" =~ /\A[1-9][0-9]*\z/
      && $value <= $maximum;
    return 0 + $value;
}

sub _metadata_lock {
    my ($file, $mode) = @_;
    # Lock a stable sibling, not the JSON inode replaced by atomic writes.
    # Windows readers must not overlap replacement (including its backup gap).
    # Keep this file in place: unlinking it could create two independent locks.
    my $lockfile = path($file)->parent->child('.metadata.lock');
    open my $lock, '>>', $lockfile
      or die "Cannot open job metadata lock <$lockfile>: $!\n";
    flock($lock, $mode)
      or die "Cannot lock job metadata <$lockfile>: $!\n";
    return $lock;
}

sub _write {
    my ($file, $data) = @_;
    my $lock = _metadata_lock($file, LOCK_EX);
    write_atomically($file, sub { path($_[0])->spew_raw($JSON->encode($data)) });
}
sub _read {
    my ($file) = @_;
    my $lock = _metadata_lock($file, LOCK_SH);
    return $JSON->decode(path($file)->slurp_raw);
}
sub _id { sha1_sum(join ':', $$, rand(), time(), {}) }
sub _resolved_path {
    my ($value) = @_;
    return unless defined $value;
    $value = "$value";
    my $resolved = abs_path($value);
    if (!defined $resolved) {
        my $candidate = path($value);
        my $parent = abs_path("@{[$candidate->parent]}");
        $resolved = defined($parent)
          ? File::Spec->catfile($parent, $candidate->basename)
          : File::Spec->rel2abs($value);
    }
    return File::Spec->canonpath($resolved);
}
sub _path_key {
    my ($value) = @_;
    my $key = _resolved_path($value);
    return unless defined $key;
    if ($^O eq 'MSWin32') {
        $key =~ tr{\\}{/};
        $key = lc $key;
    }
    return $key;
}
sub _same_path {
    my ($left, $right) = @_;
    return defined($left) && defined($right) && _path_key($left) eq _path_key($right);
}
sub _worker_lock {
    my ($dir) = @_;
    open my $fh, '>>', path($dir, '.worker.lock') or die "Cannot lock run folder\n";
    flock($fh, LOCK_EX | LOCK_NB) or die "A previous conversion is still finishing; wait before reopening this workspace\n";
    return $fh;
}

sub _recover_publication {
    my ($dir, $status) = @_;
    my $journal = path($dir, 'publication.json');
    return $status unless -f $journal;
    my $publication = _read($journal);



( run in 1.088 second using v1.01-cache-2.11-cpan-3f962061493 )