File Coverage

inc/Test/SharedFork/Store.pm
Criterion Covered Total %
statement 63 75 84.0
branch 12 26 46.1
condition n/a
subroutine 16 18 88.8
pod 0 8 0.0
total 91 127 71.6


line stmt bran cond sub pod time code
1             #line 1
2 1     1   9 package Test::SharedFork::Store;
  1         2  
  1         55  
3 1     1   6 use strict;
  1         2  
  1         82  
4 1     1   5 use warnings;
  1         2  
  1         22  
5 1     1   13 use Storable ();
  1         3  
  1         1881  
6 1     1   3455 use Fcntl ':seek', ':DEFAULT', ':flock';
  1         39118  
  1         68  
7 1     1   18 use File::Temp ();
  1         2  
  1         1140  
8             use IO::Handle;
9              
10 1     1 0 2 sub new {
11 1         4 my $class = shift;
12 1         6 my %args = @_;
13 1         725 my $filename = File::Temp::tmpnam();
14 1         6 my $self = bless {callback_on_open => $args{cb}, filename => $filename, lock => 0, pid => $$, ppid => $$}, $class;
15             $self->open();
16              
17 1         10 # initialize
18             Storable::nstore_fd(+{
19             array => [],
20             scalar => 0,
21             }, $self->{fh});
22 1         213  
23             return $self;
24             }
25              
26 1     1 0 3 sub open {
27 1 50       11 my $self = shift;
28 1         5 if (my $cb = $self->{callback_on_open}) {
29             $cb->($self);
30 1 50       609 }
31 1         19 sysopen my $fh, $self->{filename}, O_RDWR|O_CREAT or die $!;
32 1         72 $fh->autoflush(1);
33             $self->{fh} = $fh;
34             }
35              
36 0     0 0 0 sub close {
37 0         0 my $self = shift;
38 0         0 close $self->{fh};
39             undef $self->{fh};
40             }
41              
42 41     41 0 76 sub get {
43             my ($self, $key) = @_;
44 41         91  
45             $self->_reopen_if_needed;
46 41     41   107 my $ret = $self->lock_cb(sub {
47 41         225 $self->get_nolock($key);
48 41         377 }, LOCK_SH);
49             return $ret;
50             }
51              
52 48     48 0 75 sub get_nolock {
53 48         144 my ($self, $key) = @_;
54 48 50       786 $self->_reopen_if_needed;
55 48         294 seek $self->{fh}, 0, SEEK_SET or die $!;
56             Storable::fd_retrieve($self->{fh})->{$key};
57             }
58              
59 7     7 0 33 sub set {
60             my ($self, $key, $val) = @_;
61 7         23  
62             $self->_reopen_if_needed;
63 7     7   24 $self->lock_cb(sub {
64 7         47 $self->set_nolock($key, $val);
65             }, LOCK_EX);
66             }
67              
68 14     14 0 27 sub set_nolock {
69             my ($self, $key, $val) = @_;
70 14         63  
71             $self->_reopen_if_needed;
72 14 50       258  
73 14         46 seek $self->{fh}, 0, SEEK_SET or die $!;
74 14         501 my $dat = Storable::fd_retrieve($self->{fh});
75             $dat->{$key} = $val;
76 14         1442120  
77 14 50       179 truncate $self->{fh}, 0;
78 14         109 seek $self->{fh}, 0, SEEK_SET or die $!;
79             Storable::nstore_fd($dat => $self->{fh});
80             }
81              
82 63     63 0 102 sub lock_cb {
83             my ($self, $cb) = @_;
84 63         117  
85             $self->_reopen_if_needed;
86 63 100       197  
87 27 50       225 if ($self->{lock}++ == 0) {
88             flock $self->{fh}, LOCK_EX or die $!;
89             }
90 63         129  
91             my $ret = $cb->();
92 63         4343  
93 63 100       175 $self->{lock}--;
94 27 50       198 if ($self->{lock} == 0) {
95             flock $self->{fh}, LOCK_UN or die $!;
96             }
97 63         341  
98             $ret;
99             }
100              
101 173     173   339 sub _reopen_if_needed {
102 173 50       2066 my $self = shift;
103 0           if ($self->{pid} != $$) { # forked, and I'm just a child.
104 0 0         $self->{pid} = $$;
105 0 0         if ($self->{lock} > 0) { # unlock! I'm not owner!
106 0           flock $self->{fh}, LOCK_UN or die $!;
107             $self->{lock} = 0;
108 0           }
109 0           $self->close();
110             $self->open();
111             }
112             }
113              
114 0     0     sub DESTROY {
115 0 0         my $self = shift;
116 0           if ($self->{ppid} eq $$) { # cleanup method only run on original process.
117             unlink $self->{filename};
118             }
119             }
120              
121             1;