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.59';
3 37     37   125 use strict;
  37         45  
  37         848  
4 37     37   120 use warnings;
  37         42  
  37         910  
5              
6             # ABSTRACT: file locker with an automatic out-of-scope unlocking mechanism
7              
8              
9 37     37   109 use Fcntl qw(:flock);
  37         46  
  37         3351  
10              
11 37     37   145 use Params::Validate;
  37         61  
  37         1514  
12 37     37   137 use POSIX qw(:errno_h);
  37         52  
  37         216  
13 37     37   29151 use Carp;
  37         44  
  37         1457  
14              
15 37     37   9094 use Ubic::Lockf::Alarm;
  37         52  
  37         946  
16              
17 37     37   154 use parent qw(Exporter);
  37         38  
  37         217  
18              
19             our @EXPORT = qw(lockf);
20              
21              
22             sub DESTROY ($) {
23 58     58   2318004 my ($self) = @_;
24 58         85 local $@;
25 58         175 my $fh = $self->{_fh};
26 58 100       831 return unless defined $fh; # already released or dissolved
27 56         473 flock $fh, LOCK_UN;
28 56         1242 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 421     421 1 2705 my ($param, $opts) = validate_pos(@_, 1, 0);
40 421   100     1311 $opts ||= {};
41 421         503 $opts = validate(@{ [ $opts ] }, {
  421         6363  
42             blocking => 0,
43             shared => 0,
44             silent => 0, # deprecated option, does nothing
45             timeout => 0,
46             mode => 0,
47             });
48 421         2740 $opts = {%defaults, %$opts};
49              
50 421         662 my ($fh, $fname);
51 421 50       1032 if (ref $param eq "") { # filename instead of filehandle
52 421 50       16749 open $fh, ">>", $param or die "Can't open $param: $!";
53 421         960 $fname = $param;
54             } else {
55 0         0 $fh = $param;
56             }
57              
58 421 100       1187 unless (_lockf($fh, $opts, $fname)) {
59 345         5443 return;
60             }
61              
62             # don't check chmod success - it can fail and it's ok
63 76 50 0     295 chmod ($opts->{mode}, ($fname || $fh)) if defined $opts->{mode};
64              
65 76         1034 return bless {
66             _fh => $fh,
67             _fname => $fname,
68             };
69             }
70              
71             sub _lockf ($$;$) {
72 421     421   716 my ($fh, $opts, $fname) = @_;
73 421   50     984 $fname ||= ''; # TODO - discover $fname from $fh, it's possible in most cases with some /proc magic
74              
75 421 50       1250 my $mode = ($opts->{shared} ? LOCK_SH : LOCK_EX);
76              
77 421 100 100     3567 if (
      33        
78             not $opts->{blocking}
79             or (defined $opts->{timeout} and not $opts->{timeout}) # timeout=0
80             ) {
81 350 100       2226 return 1 if flock ($fh, $mode | LOCK_NB);
82 345 50       6026 return 0 if ($! == EWOULDBLOCK);
83 0   0     0 croak "flock ".($fname || '')." failed: $!";
84             }
85              
86 71 100       693 unless (flock ($fh, $mode | LOCK_NB)) {
87 1         17 my $msg = "$fname already locked, wait...";
88 1 50       10 if (-t STDOUT) {
89 0         0 print $msg;
90             }
91             } else {
92 70         287 return 1;
93             }
94              
95 1 50       7 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       900685 flock $fh, $mode or die "flock failed: $!";
101             }
102 1         14 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 18     18 1 341 my $self = shift;
113 18         618 undef $self->{_fh};
114             }
115              
116             1;
117              
118             __END__