File Coverage

lib/Ubic/Lockf.pm
Criterion Covered Total %
statement 58 67 86.5
branch 18 28 64.2
condition 7 15 46.6
subroutine 12 14 85.7
pod 3 3 100.0
total 98 127 77.1


line stmt bran cond sub pod time code
1             package Ubic::Lockf;
2             $Ubic::Lockf::VERSION = '1.58_01'; # TRIAL
3 39     39   176 use strict;
  39         65  
  39         1210  
4 39     39   190 use warnings;
  39         59  
  39         1304  
5              
6             # ABSTRACT: file locker with an automatic out-of-scope unlocking mechanism
7              
8              
9 39     39   177 use Fcntl qw(:flock);
  39         70  
  39         4782  
10              
11 39     39   220 use Params::Validate;
  39         46  
  39         2283  
12 39     39   231 use POSIX qw(:errno_h);
  39         72  
  39         278  
13 39     39   46306 use Carp;
  39         66  
  39         2470  
14              
15 39     39   14043 use Ubic::Lockf::Alarm;
  39         96  
  39         1449  
16              
17 39     39   237 use parent qw(Exporter);
  39         62  
  39         266  
18              
19             our @EXPORT = qw(lockf);
20              
21              
22             sub DESTROY ($) {
23 64     64   25878373 my ($self) = @_;
24 64         103 local $@;
25 64         226 my $fh = $self->{_fh};
26 64 100       1264 return unless defined $fh; # already released or dissolved
27 62         591 flock $fh, LOCK_UN;
28 62         1896 delete $self->{_fh}; # closes the file if opened by us
29             }
30              
31             my %defaults = (
32             shared => 0,
33             blocking => 1,
34             timeout => undef,
35             mode => undef,
36             );
37              
38             sub lockf ($;$) {
39 470     470 1 4178 my ($param, $opts) = validate_pos(@_, 1, 0);
40 470   100     2549 $opts ||= {};
41 470         631 $opts = validate(@{ [ $opts ] }, {
  470         9191  
42             blocking => 0,
43             shared => 0,
44             silent => 0, # deprecated option, does nothing
45             timeout => 0,
46             mode => 0,
47             });
48 470         5276 $opts = {%defaults, %$opts};
49              
50 470         1213 my ($fh, $fname);
51 470 50       1205 if (ref $param eq "") { # filename instead of filehandle
52 470 50       21669 open $fh, ">>", $param or die "Can't open $param: $!";
53 470         1255 $fname = $param;
54             } else {
55 0         0 $fh = $param;
56             }
57              
58 470 100       1495 unless (_lockf($fh, $opts, $fname)) {
59 384         7588 return;
60             }
61              
62             # don't check chmod success - it can fail and it's ok
63 86 50 0     389 chmod ($opts->{mode}, ($fname || $fh)) if defined $opts->{mode};
64              
65 86         1580 return bless {
66             _fh => $fh,
67             _fname => $fname,
68             };
69             }
70              
71             sub _lockf ($$;$) {
72 470     470   956 my ($fh, $opts, $fname) = @_;
73 470   50     1134 $fname ||= ''; # TODO - discover $fname from $fh, it's possible in most cases with some /proc magic
74              
75 470 50       1449 my $mode = ($opts->{shared} ? LOCK_SH : LOCK_EX);
76              
77 470 100 100     4248 if (
      33        
78             not $opts->{blocking}
79             or (defined $opts->{timeout} and not $opts->{timeout}) # timeout=0
80             ) {
81 389 100       2850 return 1 if flock ($fh, $mode | LOCK_NB);
82 384 50       7034 return 0 if ($! == EWOULDBLOCK);
83 0   0     0 croak "flock ".($fname || '')." failed: $!";
84             }
85              
86 81 100       985 unless (flock ($fh, $mode | LOCK_NB)) {
87 1         16 my $msg = "$fname already locked, wait...";
88 1 50       17 if (-t STDOUT) {
89 0         0 print $msg;
90             }
91             } else {
92 80         450 return 1;
93             }
94              
95 1 50       15 if ($opts->{timeout}) {
96 0     0   0 local $SIG{ALRM} = sub { croak "flock $fname failed: timed out" };
  0         0  
97 0         0 my $alarm = Ubic::Lockf::Alarm->new($opts->{timeout});
98 0 0       0 flock $fh, $mode or die "flock failed: $!";
99             } else {
100 1 50       904453 flock $fh, $mode or die "flock failed: $!";
101             }
102 1         18 return 1;
103             }
104              
105             sub name($)
106             {
107 0     0 1 0 my $self = shift;
108 0         0 return $self->{_fname};
109             }
110              
111             sub dissolve {
112 21     21 1 484 my $self = shift;
113 21         977 undef $self->{_fh};
114             }
115              
116             1;
117              
118             __END__