File Coverage

blib/lib/Dev/Util/Sem.pm
Criterion Covered Total %
statement 46 47 97.8
branch 6 8 75.0
condition 11 15 73.3
subroutine 10 10 100.0
pod 2 2 100.0
total 75 82 91.4


line stmt bran cond sub pod time code
1             package Dev::Util::Sem;
2              
3 1     1   772 use Dev::Util::Syntax;
  1         2  
  1         22  
4 1     1   761 use Dev::Util::File qw(mk_temp_dir dir_writable dir_suffix_slash);
  1         5  
  1         148  
5 1     1   9 use Exporter qw(import);
  1         2  
  1         36  
6              
7 1     1   601 use FileHandle;
  1         3009  
  1         10  
8 1     1   412 use Carp();
  1         2  
  1         33  
9 1     1   6 use Fcntl 'LOCK_EX';
  1         2  
  1         1165  
10              
11             our $VERSION = version->declare("v2.19.35");
12              
13             our @EXPORT_OK = qw(
14             new
15             unlock
16             );
17              
18             our %EXPORT_TAGS = ( all => \@EXPORT_OK );
19              
20             ########################################
21             # Methods #
22             ########################################
23              
24             sub new {
25 5     5 1 74 my $class = shift(@_);
26 5   66     266 my $filespec = shift(@_) || Carp::croak("What filespec?");
27 4   100     17 my $timeout = shift || 60;
28              
29 4         31 my $lock_dir_parent = _get_locks_dir($filespec);
30              
31 4     1   80 local $SIG{ ALRM } = sub { die "Timeout aquiring the lock on $filespec\n" };
  1         72  
32 4 50       37 alarm $timeout if ( $timeout > 0 );
33              
34 4         19 $filespec =~ s{^.*/}{};
35 4         15 $filespec = $lock_dir_parent . $filespec;
36              
37 4         37 my $fh = FileHandle->new;
38 4 50       194 $fh->open( '>' . $filespec )
39             or Carp::croak("Can't open semaphore file $filespec: $!\n");
40 4         10336 chmod 0666, $filespec; # assuming you want it a+rw
41              
42 4         783 flock $fh, LOCK_EX;
43              
44 4         1001789 alarm 0;
45 3   33     89 return bless { file => $filespec, 'fh' => $fh }, ref($class) || $class;
46             }
47              
48             sub unlock {
49 3   50 3 1 1508 close( delete $_[0]{ 'fh' } or return 0 );
50 3         637 unlink( $_[0]{ file } );
51 3         865 return 1;
52             }
53              
54             sub _get_locks_dir {
55 7   100 7   594 my $spec = shift || undef;
56 7         26 my @locks_dirs = qw(/var/lock /var/locks /run/lock /tmp);
57              
58 7         40 my $dirfile_re = qr<^ ( (?: .* / (?: \.\.?\z )? )? ) ([^/]*) >xs;
59 7         16 my ( $spec_dir, $spec_file );
60              
61             # add spec's dir to list of possible lock dirs
62 7 100 100     43 if ( defined $spec && $spec =~ m{/} ) {
63 3         64 ( $spec_dir, $spec_file ) = ( $spec =~ $dirfile_re );
64 3         12 unshift @locks_dirs, $spec_dir;
65             }
66              
67             # find first writable lock dir
68 7         38 foreach my $locks_dir (@locks_dirs) {
69 8 100       29 if ( dir_writable($locks_dir) ) {
70 7         21 return dir_suffix_slash($locks_dir);
71             }
72             }
73 0           Carp::croak("Could not find a writable dir to make lock.$!\n");
74             }
75              
76             1;
77              
78             =pod
79              
80             =encoding utf-8
81              
82             =head1 NAME
83              
84             Dev::Util::Sem - Module to do Semaphore locking
85              
86             =head1 VERSION
87              
88             Version v2.19.35
89              
90             =head1 SYNOPSIS
91              
92             To ensure that only one instance of a program runs at a time,
93             create a semaphore lock file. A second instance will wait until
94             the first lock is unlocked before it can proceed or it times out.
95              
96             use Dev::Util::Sem;
97              
98             my $sem = Sem->new('mylock.sem');
99             ...
100             $sem->unlock;
101              
102             =head1 EXPORT
103              
104             new
105             unlock
106              
107             =head1 METHODS
108              
109             =head2 B<new>
110              
111             Initialize semaphore. You can specify the full path to the lock,
112             and if the directory you specify exists and is writable then the
113             lock file will be placed there. If you don't specify a directory
114             or the one you specified is not writable, then a list of alternate
115             lock dirs will be tried.
116              
117             my $sem1 = Sem->new('/wherever/locks/mylock1.sem');
118             my $sem2 = Sem->new('mylock2.sem', TIMEOUT);
119              
120             C<TIMEOUT> number of seconds to wait while trying to acquire a lock. Default = 60 seconds
121              
122             Alternate lock dirs:
123              
124             qw(/var/lock /var/locks /run/lock /tmp);
125              
126             =head2 B<unlock>
127              
128             Unlock semaphore and delete lock file.
129              
130             $sem->unlock;
131              
132             =head1 AUTHOR
133              
134             Matt Martini, C<< <matt at imaginarywave.com> >>
135              
136             =head1 BUGS
137              
138             C<flock> may not work over C<nfs>.
139              
140             Please report any bugs or feature requests to C<bug-dev-util at rt.cpan.org>, or through
141             the web interface at L<https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Dev-Util>. I will
142             be notified, and then you'll automatically be notified of progress on your bug as I make changes.
143              
144             =head1 SUPPORT
145              
146             You can find documentation for this module with the perldoc command.
147              
148             perldoc Dev::Util::Backup
149              
150             You can also look for information at:
151              
152             =over 4
153              
154             =item * RT: CPAN's request tracker (report bugs here)
155              
156             L<https://rt.cpan.org/NoAuth/Bugs.html?Dist=Dev-Util>
157              
158             =item * Search CPAN
159              
160             L<https://metacpan.org/release/Dev-Util>
161              
162             =back
163              
164             =head1 ACKNOWLEDGMENTS
165              
166             =head1 LICENSE AND COPYRIGHT
167              
168             This software is Copyright © 2001-2025 by Matt Martini.
169              
170             This is free software, licensed under:
171              
172             The GNU General Public License, Version 3, June 2007
173              
174             =cut
175              
176             __END__