File Coverage

blib/lib/File/Flock/Tiny.pm
Criterion Covered Total %
statement 54 54 100.0
branch 13 16 81.2
condition 8 9 88.8
subroutine 15 15 100.0
pod 3 3 100.0
total 93 97 95.8


line stmt bran cond sub pod time code
1             package File::Flock::Tiny;
2              
3 5     5   119641 use 5.008;
  5         20  
  5         187  
4 5     5   29 use strict;
  5         5  
  5         150  
5 5     5   28 use warnings;
  5         15  
  5         116  
6 5     5   21 use Carp;
  5         10  
  5         541  
7 5     5   6215 use IO::Handle;
  5         43613  
  5         274  
8 5     5   39 use Fcntl qw(:flock);
  5         12  
  5         2338  
9              
10             =head1 NAME
11              
12             File::Flock::Tiny - yet another flock package
13              
14             =cut
15              
16             our $VERSION = '0.14';
17             $VERSION = eval $VERSION;
18              
19             =head1 SYNOPSIS
20              
21             my $lock = File::Flock::Tiny->lock($file);
22             ... # do something
23             $lock->release;
24              
25             =head1 DESCRIPTION
26              
27             Simple wrapper around L for ease of use.
28              
29             =head1 CLASS METHODS
30              
31             =cut
32              
33             sub _open_file {
34 39     39   82 my $file = shift;
35 39         64 my $fh;
36 39 100 100     766 if ( ref $file && ( ref $file eq 'GLOB' || $file->isa("IO::Handle") ) ) {
      66        
37 8 50       52 $fh = IO::Handle->new_from_fd( $file, ">>" ) or croak "Coundn't dupe file: $!";
38             }
39             else {
40 31 50       565 open $fh, ">>", $file or croak "Couldn't open file: $!";
41             }
42 39         4163 return $fh;
43             }
44              
45             =head2 File::Flock::Tiny->lock($file)
46              
47             Acquire exclusive lock on the file. I<$file> may be a file name or an opened
48             file handler. If a filename given and the file doesn't exist it will be
49             created. The method returns a lock object, the file remains locked until this
50             object goes out of the scope, or till you call I method on it.
51              
52             =cut
53              
54             sub lock {
55 23     23 1 52906 my $fh = _open_file( $_[1] );
56 23 50       199 flock $fh, LOCK_EX or croak $!;
57 23         133 return bless $fh, "File::Flock::Tiny::Lock";
58             }
59              
60             =head2 File::Flock::Tiny->trylock($file)
61              
62             Same as I, but doesn't block and returns immediately, if the lock can not
63             be acquired returns undef.
64              
65             =cut
66              
67             sub trylock {
68 16     16 1 9486 my $fh = _open_file( $_[1] );
69 16         64 bless $fh, "File::Flock::Tiny::Lock";
70 16 100       176 return unless flock $fh, LOCK_EX | LOCK_NB;
71 10         34 return $fh;
72             }
73              
74             =head2 File::Flock::Tiny->write_pid($file)
75             X
76              
77             Try to lock the file and save the process ID into it. Returns the lock object,
78             or undef if the file was already locked. The lock returned by I will
79             be automatically released when the object goes out of the scope in the process
80             that locked the pid file, in child processes you can release the lock
81             explicitely.
82              
83             =cut
84              
85             sub write_pid {
86 4     4 1 419622 my ( $class, $file ) = @_;
87 4         300 my $lock = $class->trylock($file);
88 4 100       30 $lock->write_pid if $lock;
89 4         72 return $lock;
90             }
91              
92             package File::Flock::Tiny::Lock;
93 5     5   5379 use parent 'IO::Handle';
  5         1544  
  5         32  
94 5     5   301 use Fcntl qw(:flock);
  5         15  
  5         1513  
95              
96             =head1 LOCK OBJECT METHODS
97              
98             Here is the list of methods that you can invoke on a lock object.
99              
100             =head2 $lock->write_pid
101              
102             Truncates locked file and saves PID into it. Also marks the lock object as tied
103             to the current process, so it only will be automatically released when goes out
104             of scope in the current process but not in any of the child processes created
105             after this call. This method may be used to create pid files for daemons, you
106             can lock file in parent process to ensure that there is no another copy of the
107             daemon running already, and then fork and write pid of the child into the file.
108             Here is the simplified example of daemonizing code:
109              
110             my $pid = File::Flock::Tiny->trylock('daemon.pid')
111             or die "Daemon already running";
112             if ( fork == 0 ) {
113             setsid;
114             if (fork) {
115             # intermediate process
116             $pid->close;
117             exit 0;
118             }
119             }
120             else {
121             # parent process
122             $pid->close;
123             exit 0;
124             }
125             # daemon process
126             # perhaps you want to close all opened files here, do not close $pid!
127             $pid->write_pid;
128              
129             It is importand to remember to close the lock file in the parent and
130             intermediate processes, otherwise the lock will be released during destruction
131             of the variable.
132              
133             =cut
134              
135             sub write_pid {
136 2     2   8 my $lock = shift;
137 2         60 $lock->truncate(0);
138 2         216 $lock->print("$$\n");
139 2         142 $lock->flush;
140 2         14 *$lock->{destroy_only_in} = $$;
141 2         16 return;
142             }
143              
144             =head2 $lock->release
145              
146             Release lock and close the file
147              
148             =cut
149              
150             sub release {
151 50     50   4172 my $lock = shift;
152 50 100       203 if ( $lock->opened ) {
153 35         503 flock $lock, LOCK_UN;
154 35         678 close $lock;
155             }
156             }
157              
158             sub DESTROY {
159 39     39   832897 my $lock = shift;
160 39 100 100     1704 unless ( *$lock->{destroy_only_in} && *$lock->{destroy_only_in} != $$ ) {
161 38         147 $lock->release;
162             }
163             }
164              
165             =head2 $lock->close
166              
167             Close the locked filehandle, but do not release the lock. Normally if you closed
168             the file it will be unlocked, but if you forked after locking the file and when
169             closed the lock in the parent process, the file will still be locked even after
170             the lock went out of the scope in the parent process. The following example
171             demonstrates the use for this method:
172              
173             {
174             my $lock = File::Flock::Tiny->lock("lockfile");
175             my $pid = fork;
176             if( $pid == 0 ) {
177             # We are in child process
178             do_something();
179             }
180             $lock->close;
181             }
182             # file still locked by child. Without $lock->close,
183             # it would be unlocked by parent when $lock went out
184             # of the scope
185              
186             Note, that this behaviour is not portable! It works on Linux and BSD, but on
187             Solaris locks are not inherited by child processes, so the file will be
188             unlocked as soon as the parent process will close it. See also description of
189             L.
190              
191             =cut
192              
193             1;
194              
195             __END__