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.60';
3 36     36   114 use strict;
  36         37  
  36         756  
4 36     36   104 use warnings;
  36         38  
  36         766  
5              
6             # ABSTRACT: file locker with an automatic out-of-scope unlocking mechanism
7              
8              
9 36     36   138 use Fcntl qw(:flock);
  36         50  
  36         2800  
10              
11 36     36   142 use Params::Validate;
  36         39  
  36         1294  
12 36     36   104 use POSIX qw(:errno_h);
  36         35  
  36         222  
13 36     36   26265 use Carp;
  36         53  
  36         1379  
14              
15 36     36   8890 use Ubic::Lockf::Alarm;
  36         46  
  36         839  
16              
17 36     36   117 use parent qw(Exporter);
  36         37  
  36         153  
18              
19             our @EXPORT = qw(lockf);
20              
21              
22             sub DESTROY ($) {
23 58     58   2149628 my ($self) = @_;
24 58         87 local $@;
25 58         175 my $fh = $self->{_fh};
26 58 100       795 return unless defined $fh; # already released or dissolved
27 56         404 flock $fh, LOCK_UN;
28 56         2289 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 406     406 1 2750 my ($param, $opts) = validate_pos(@_, 1, 0);
40 406   100     1246 $opts ||= {};
41 406         490 $opts = validate(@{ [ $opts ] }, {
  406         6055  
42             blocking => 0,
43             shared => 0,
44             silent => 0, # deprecated option, does nothing
45             timeout => 0,
46             mode => 0,
47             });
48 406         2567 $opts = {%defaults, %$opts};
49              
50 406         748 my ($fh, $fname);
51 406 50       1007 if (ref $param eq "") { # filename instead of filehandle
52 406 50       16325 open $fh, ">>", $param or die "Can't open $param: $!";
53 406         972 $fname = $param;
54             } else {
55 0         0 $fh = $param;
56             }
57              
58 406 100       1237 unless (_lockf($fh, $opts, $fname)) {
59 331         6043 return;
60             }
61              
62             # don't check chmod success - it can fail and it's ok
63 75 50 0     220 chmod ($opts->{mode}, ($fname || $fh)) if defined $opts->{mode};
64              
65 75         850 return bless {
66             _fh => $fh,
67             _fname => $fname,
68             };
69             }
70              
71             sub _lockf ($$;$) {
72 406     406   583 my ($fh, $opts, $fname) = @_;
73 406   50     766 $fname ||= ''; # TODO - discover $fname from $fh, it's possible in most cases with some /proc magic
74              
75 406 50       978 my $mode = ($opts->{shared} ? LOCK_SH : LOCK_EX);
76              
77 406 100 100     3047 if (
      33        
78             not $opts->{blocking}
79             or (defined $opts->{timeout} and not $opts->{timeout}) # timeout=0
80             ) {
81 336 100       2324 return 1 if flock ($fh, $mode | LOCK_NB);
82 331 50       5074 return 0 if ($! == EWOULDBLOCK);
83 0   0     0 croak "flock ".($fname || '')." failed: $!";
84             }
85              
86 70 100       567 unless (flock ($fh, $mode | LOCK_NB)) {
87 1         20 my $msg = "$fname already locked, wait...";
88 1 50       16 if (-t STDOUT) {
89 0         0 print $msg;
90             }
91             } else {
92 69         269 return 1;
93             }
94              
95 1 50       9 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       900557 flock $fh, $mode or die "flock failed: $!";
101             }
102 1         12 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 17     17 1 306 my $self = shift;
113 17         661 undef $self->{_fh};
114             }
115              
116             1;
117              
118             __END__