File Coverage

blib/lib/JIP/LockSocket.pm
Criterion Covered Total %
statement 47 47 100.0
branch 17 18 94.4
condition 5 6 83.3
subroutine 14 14 100.0
pod 0 4 0.0
total 83 89 93.2


line stmt bran cond sub pod time code
1             package JIP::LockSocket;
2              
3 1     1   1686 use 5.006;
  1         4  
  1         56  
4 1     1   9 use strict;
  1         2  
  1         53  
5 1     1   18 use warnings;
  1         1  
  1         55  
6 1     1   447 use JIP::ClassField;
  1         1480  
  1         6  
7 1     1   80 use Carp qw(croak);
  1         2  
  1         54  
8 1     1   4 use English qw(-no_match_vars);
  1         1  
  1         7  
9 1     1   1122 use Socket qw(inet_aton pack_sockaddr_in PF_INET SOCK_STREAM);
  1         4807  
  1         641  
10              
11             our $VERSION = '0.02';
12              
13             map { has $_ => (get => '+', set => '-') } qw(port addr socket is_locked);
14              
15             sub new {
16 16     16 0 7105 my ($class, %param) = @ARG;
17              
18             # Mandatory options
19 16 100       191 croak qq{Mandatory argument "port" is missing\n}
20             unless exists $param{'port'};
21              
22             # Check "port"
23 15         23 my $port = $param{'port'};
24 15 100 100     361 croak qq{Bad argument "port"\n}
25             unless defined $port and $port =~ m{^\d+$}x;
26              
27             # Check "addr"
28 13 100 66     74 my $addr = (exists $param{'addr'} and length $param{'addr'})
29             ? $param{'addr'} : '127.0.0.1';
30              
31             # Class to object
32 13         60 return bless({}, $class)
33             ->_set_is_locked(0)
34             ->_set_port($port)
35             ->_set_addr($addr)
36             ->_set_socket(undef);
37             }
38              
39             # Lock or raise an exception
40             sub lock {
41 7     7 0 92 my $self = shift;
42              
43             # Re-locking changes nothing
44 7 100       16 return $self if $self->is_locked;
45              
46 6         32 my $socket = $self->_init_socket;
47              
48 6 100       24 bind($socket, pack_sockaddr_in($self->port, $self->_get_inet_addr))
49             or croak(sprintf qq{Can't lock port "%s": %s\n}, $self->port, $OS_ERROR);
50              
51 5         86 return $self->_set_socket($socket)->_set_is_locked(1);
52             }
53              
54             # Or just return undef
55             sub try_lock {
56 3     3 0 39 my $self = shift;
57              
58             # Re-locking changes nothing
59 3 100       5 return $self if $self->is_locked;
60              
61 2         11 my $socket = $self->_init_socket;
62              
63 2 100       10 if (bind($socket, pack_sockaddr_in($self->port, $self->_get_inet_addr))) {
64 1         12 return $self->_set_socket($socket)->_set_is_locked(1);
65             }
66             else {
67 1         29 return;
68             }
69             }
70              
71             # You can manually unlock
72             sub unlock {
73 15     15 0 38 my $self = shift;
74              
75             # Re-unlocking changes nothing
76 15 100       37 return $self if not $self->is_locked;
77              
78 6         34 return $self->_set_socket(undef)->_set_is_locked(0);
79             }
80              
81             # unlocking on scope exit
82             sub DESTROY {
83 13     13   506 my $self = shift;
84 13         27 return $self->unlock;
85             }
86              
87             sub _get_inet_addr {
88 8     8   36 my $self = shift;
89 8         15 return inet_aton($self->addr);
90             }
91              
92             sub _init_socket {
93 8 50   8   1213 socket(my $socket, PF_INET, SOCK_STREAM, getprotobyname('tcp'))
94             or croak(sprintf qq{Can't init socket: %s\n}, $OS_ERROR);
95              
96 8         24 return $socket;
97             }
98              
99             1;
100              
101             __END__