File Coverage

inc/Test/SharedFork/Store.pm
Criterion Covered Total %
statement 52 72 72.2
branch 9 30 30.0
condition 1 2 50.0
subroutine 14 17 82.3
pod 0 6 0.0
total 76 127 59.8


line stmt bran cond sub pod time code
1             #line 1
2 2     2   11 package Test::SharedFork::Store;
  2         5  
  2         68  
3 2     2   11 use strict;
  2         4  
  2         59  
4 2     2   11 use warnings;
  2         3  
  2         51  
5 2     2   11 use Storable ();
  2         4  
  2         1310  
6 2     2   3227 use Fcntl ':seek', ':DEFAULT', ':flock';
  2         44305  
  2         54  
7 2     2   19 use File::Temp ();
  2         5  
  2         1726  
8             use IO::Handle;
9              
10 2     2 0 4 sub new {
11 2         7 my $class = shift;
12 2         10 my %args = @_;
13             my $filename = File::Temp::tmpnam();
14 2   50     2486  
15             my $init = Storable::dclone($args{init} || +{});
16 2         25  
17             my $self = bless {
18             callback_on_open => $args{cb},
19             filename => $filename,
20             lock => 0,
21             pid => $$,
22             ppid => $$,
23 2         11 }, $class;
24             $self->open();
25              
26 2 50       16 # initialize
27             Storable::nstore_fd($init, $self->{fh}) or die "Cannot write initialize data to $filename";
28 2         490  
29             return $self;
30             }
31              
32 2     2 0 4 sub open {
33 2 50       21 my $self = shift;
34 2         8 if (my $cb = $self->{callback_on_open}) {
35             $cb->($self);
36 2 50       258 }
37 2         24 sysopen my $fh, $self->{filename}, O_RDWR|O_CREAT or die $!;
38 2         111 $fh->autoflush(1);
39             $self->{fh} = $fh;
40             }
41              
42 0     0 0 0 sub close {
43 0         0 my $self = shift;
44 0         0 close $self->{fh};
45             undef $self->{fh};
46             }
47              
48 6     6 0 10 sub get {
49 6         16 my ($self, $key) = @_;
50 6 50       46 $self->_reopen_if_needed;
51 6         24 seek $self->{fh}, 0, SEEK_SET or die $!;
52             Storable::fd_retrieve($self->{fh})->{$key};
53             }
54              
55 0     0 0 0 sub set {
56             my ($self, $key, $val) = @_;
57 0         0  
58             $self->_reopen_if_needed;
59 0 0       0  
60 0         0 seek $self->{fh}, 0, SEEK_SET or die $!;
61 0         0 my $dat = Storable::fd_retrieve($self->{fh});
62             $dat->{$key} = $val;
63 0         0  
64 0 0       0 truncate $self->{fh}, 0;
65 0 0       0 seek $self->{fh}, 0, SEEK_SET or die $!;
66             Storable::nstore_fd($dat => $self->{fh}) or die "Cannot store data to $self->{filename}";
67             }
68              
69 6     6 0 10 sub get_lock {
70 6         35 my ($self, ) = @_;
71             Test::SharedFork::Store::Locker->new($self);
72             }
73              
74 12     12   15 sub _reopen_if_needed {
75 12 50       136 my $self = shift;
76 0         0 if ($self->{pid} != $$) { # forked, and I'm just a child.
77 0 0       0 $self->{pid} = $$;
78 0 0       0 if ($self->{lock} > 0) { # unlock! I'm not owner!
79 0         0 flock $self->{fh}, LOCK_UN or die $!;
80             $self->{lock} = 0;
81 0         0 }
82 0         0 $self->close();
83             $self->open();
84             }
85             }
86              
87 0     0   0 sub DESTROY {
88 0 0       0 my $self = shift;
89 0         0 if ($self->{ppid} eq $$) { # cleanup method only run on original process.
90             unlink $self->{filename};
91             }
92             }
93              
94             package # hide from pause
95             Test::SharedFork::Store::Locker;
96 2     2   15  
  2         4  
  2         621  
97             use Fcntl ':flock';
98              
99 6     6   14 sub new {
100             my ($class, $store) = @_;
101 6         18  
102             $store->_reopen_if_needed;
103 6 50       122  
104 6 50       60 if ($store->{lock}++ == 0) {
105             flock $store->{fh}, LOCK_EX or die $!;
106             }
107 6         37  
108             bless { store => $store }, $class;
109             }
110              
111 6     6   168 sub DESTROY {
112             my ($self) = @_;
113 6         21  
114 6 50       20 $self->{store}->{lock}--;
115 6 50       92 if ($self->{store}->{lock} == 0) {
116             flock $self->{store}->{fh}, LOCK_UN or die $!;
117             }
118             }
119              
120             1;