File Coverage

blib/lib/Mail/Box/Locker/DotLock.pm
Criterion Covered Total %
statement 48 61 78.6
branch 12 34 35.2
condition 1 6 16.6
subroutine 12 13 92.3
pod 4 5 80.0
total 77 119 64.7


line stmt bran cond sub pod time code
1             # This code is part of Perl distribution Mail-Box version 4.01.
2             # The POD got stripped from this file by OODoc version 3.05.
3             # For contributors see file ChangeLog.
4              
5             # This software is copyright (c) 2001-2025 by Mark Overmeer.
6              
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             # SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
10              
11              
12             package Mail::Box::Locker::DotLock;{
13             our $VERSION = '4.01';
14             }
15              
16 2     2   3131 use parent 'Mail::Box::Locker';
  2         5  
  2         15  
17              
18 2     2   117 use strict;
  2         4  
  2         36  
19 2     2   7 use warnings;
  2         3  
  2         116  
20              
21 2     2   8 use Log::Report 'mail-box', import => [ qw/__x error fault warning/ ];
  2         3  
  2         15  
22              
23 2     2   378 use File::Spec::Functions qw/catfile/;
  2         4  
  2         99  
24 2     2   9 use Errno qw/EEXIST/;
  2         2  
  2         159  
25 2     2   6 use Fcntl qw/O_CREAT O_EXCL O_WRONLY O_NONBLOCK/;
  2         4  
  2         1229  
26              
27             #--------------------
28              
29             sub init($)
30 1     1 0 16 { my ($self, $args) = @_;
31 1 50       4 $args->{file} = $args->{dotlock_file} if $args->{dotlock_file};
32 1         8 $self->SUPER::init($args);
33             }
34              
35             sub name() { 'DOTLOCK' }
36              
37             #--------------------
38              
39             sub folder(;$)
40 1     1 1 4 { my $self = shift;
41 1 50 33     64 @_ && $_[0] or return $self->SUPER::folder;
42              
43 1         3 my $folder = shift;
44 1 50       9 unless(defined $self->filename)
45 0         0 { my $org = $folder->organization;
46              
47 0 0       0 my $filename
    0          
48             = $org eq 'FILE' ? $folder->filename . '.lock'
49             : $org eq 'DIRECTORY'? catfile($folder->directory, '.lock')
50             : error __x"Dotlock requires a lock file name.";
51              
52 0         0 $self->filename($filename);
53             }
54              
55 1         7 $self->SUPER::folder($folder);
56             }
57              
58             #--------------------
59              
60             sub unlock()
61 1     1 1 4022 { my $self = shift;
62 1 50       5 $self->hasLock
63             or return $self;
64              
65 1         6 my $lock = $self->filename;
66              
67 1 50       165 unlink $lock
68             or warning __x"couldn't remove lockfile {file}: {rc}", file => $lock, rc => $!;
69              
70 1         14 $self->SUPER::unlock;
71 1         4 $self;
72             }
73              
74              
75             sub _try_lock($)
76 1     1   3 { my ($self, $lockfile) = @_;
77 1 50       20 return if -e $lockfile;
78              
79 1 50       8 my $flags = $^O eq 'MSWin32' ? O_CREAT|O_EXCL|O_WRONLY : O_CREAT|O_EXCL|O_WRONLY|O_NONBLOCK;
80 1         2 my $lock;
81 1 50       238 sysopen $lock, $lockfile, $flags, 0600
82             and $lock->close, return 1;
83              
84 0 0       0 $! == EEXIST
85             or fault __x"lockfile {file} can never be created", file => $lockfile;
86              
87 0         0 1;
88             }
89              
90             sub lock()
91 2     2 1 2414 { my $self = shift;
92              
93 2         9 my $lockfile = $self->filename;
94 2 100       12 $self->hasLock
95             and warning(__x"folder already locked with file {file}.", file => $lockfile), return 1;
96              
97 1         6 my $timeout = $self->timeout;
98 1 50       5 my $end = $timeout eq 'NOTIMEOUT' ? -1 : $timeout;
99 1         5 my $expire = $self->expires/86400; # in days for -A
100              
101 1         3 while(1)
102             {
103 1 50       5 return $self->SUPER::lock
104             if $self->_try_lock($lockfile);
105              
106 0 0 0       if(-e $lockfile && -A $lockfile > $expire)
107 0 0         { unlink $lockfile
108             or fault __x"failed to remove expired lockfile {file}", file => $lockfile;
109              
110 0           warning __x"removed expired lockfile {file}.", file => $lockfile;
111 0           redo;
112             }
113              
114 0 0         --$end or last;
115 0           sleep 1;
116             }
117              
118 0           return 0;
119             }
120              
121 0     0 1   sub isLocked() { -e shift->filename }
122              
123             1;