File Coverage

blib/lib/Sys/RunAlone/Flexible.pm
Criterion Covered Total %
statement 33 52 63.4
branch 7 30 23.3
condition 1 11 9.0
subroutine 9 9 100.0
pod 1 1 100.0
total 51 103 49.5


line stmt bran cond sub pod time code
1             ## no critic: ValuesAndExpressions::ProhibitCommaSeparatedStatements
2              
3             package Sys::RunAlone::Flexible;
4              
5             our $DATE = '2019-09-03'; # DATE
6             our $VERSION = '0.004'; # VERSION
7              
8             # make sure we're strict and verbose as possible
9 2     2   56141 use strict;
  2         10  
  2         49  
10 2     2   9 use warnings;
  2         4  
  2         47  
11              
12             # make sure we know how to lock
13 2     2   9 use Fcntl ':flock';
  2         3  
  2         293  
14              
15             # process local storage
16             my $silent;
17             my $retry;
18              
19             # this holds the package namespace of the calling script
20             our $pkg;
21              
22             # this holds the package namespace of where a tag was found. it will always be
23             # main::DATA for __END__, or the namespace of the calling script if __DATA__
24             our $data_pkg = 'main::DATA';
25              
26             sub lock {
27 2     2   22 no warnings;
  2         4  
  2         64  
28 2     2   9 no strict 'refs';
  2         3  
  2         867  
29              
30             # the environment variables have to be checked here since import doesn't
31             # execute when brought in via "require".
32             # NOTE: the evironment variables will override options that were passed in
33             # via "use" if present!
34 1 50   1 1 4 $silent = $ENV{SILENT_SYS_RUNALONE} if exists $ENV{SILENT_SYS_RUNALONE};
35 1 50       14 $retry = $ENV{RETRY_SYS_RUNALONE} if exists $ENV{RETRY_SYS_RUNALONE};
36              
37             # skipping
38 1 50       6 if ( my $skip= $ENV{SKIP_SYS_RUNALONE} ) {
    50          
39 0 0 0     0 print STDERR "Skipping " . __PACKAGE__ . " check for '$0'\n"
40             if !$silent and $skip > 1;
41              
42 0         0 return;
43             }
44             elsif ( tell(*main::DATA) == -1 ) {
45              
46             # if we reach this then the __END__ tag does not exist. swap in the
47             # calling script namespace to see if the __DATA__ tag exists.
48 0         0 $data_pkg = $pkg . '::DATA';
49 0 0       0 if ( ( tell( *{$data_pkg} ) == -1 ) ) {
  0         0  
50 0         0 print STDERR "Add __END__ or __DATA__ to end of script '$0'"
51             . " to be able use the features of Sys::RunALone\n";
52 0         0 exit 2;
53             }
54             }
55              
56             # are we alone? $data_pkg will be set to wherever an appropriate tag was.
57 1 50       2 if ( !flock *{$data_pkg}, LOCK_EX | LOCK_NB ) {
  1         18  
58              
59             # need to retry
60 0 0       0 if ($retry) {
61 0 0       0 print STDERR "Retrying lock attempt ...\n" unless $silent;
62 0         0 my ( $times, $sleep )= split ',', $retry;
63 0   0     0 $sleep ||= 1;
64 0         0 while ( $times-- ) {
65 0         0 sleep $sleep;
66              
67             # we're alone!
68 0 0       0 goto ALLOK if flock *{$data_pkg}, LOCK_EX | LOCK_NB;
  0         0  
69             }
70 0 0       0 print STDERR "Retrying lock failed ...\n" unless $silent;
71             }
72              
73             # we're done
74 0 0       0 print STDERR "A copy of '$0' is already running\n" if !$silent;
75 0         0 exit 1;
76             }
77              
78             ALLOK:
79 1         5 return;
80             }
81              
82             #-------------------------------------------------------------------------------
83             #
84             # Standard Perl functionality
85             #
86             #-------------------------------------------------------------------------------
87             # import
88             #
89             # IN: 1 class (not used)
90             # 2 .. N options (default: none)
91              
92             sub import {
93 1     1   8 shift;
94              
95             # support obsolete form of silencing
96 1 0 33     4 $silent= 1, return if @_ == 1 and $_[0] and $_[0] eq 'silent';
      0        
97              
98             # huh?
99 1 50       4 die "Must specify even number of parameters" if ( @_ & 1 ) == 1;
100              
101             # obtain parameters
102 1         2 my %args= @_;
103 1         3 $silent= delete $args{silent};
104 1         1 $retry= delete $args{retry};
105              
106             # sanity check
107 1 50       5 if ( my @huh= sort keys %args ) {
108 0         0 die "Don't know what to do with: @huh";
109             }
110              
111 1         8 return;
112             } #import
113              
114             {
115             # it is at this point we can get the correct package namespace of the
116             # calling script
117             my @call_info = caller(0);
118             $pkg = $call_info[0];
119              
120             # to shut up the 'Too late to run INIT block' warning
121 2     2   14 no warnings 'void';
  2         3  
  2         140  
122             INIT {
123 1     1   71 lock();
124             }
125             }
126              
127             # satisfy -require-
128             1;
129              
130             # ABSTRACT: make sure only one invocation of a script is active at a time
131              
132             __END__