File Coverage

blib/lib/Test/SharedFork/Store.pm
Criterion Covered Total %
statement 67 72 93.0
branch 16 30 53.3
condition 1 2 50.0
subroutine 16 17 94.1
pod 0 6 0.0
total 100 127 78.7


line stmt bran cond sub pod time code
1             package Test::SharedFork::Store;
2 31     31   150 use strict;
  31         62  
  31         847  
3 31     31   162 use warnings;
  31         56  
  31         812  
4 31     31   224 use Storable ();
  31         70  
  31         685  
5 31     31   145 use Fcntl ':seek', ':DEFAULT', ':flock';
  31         52  
  31         17086  
6 31     31   711568 use File::Temp ();
  31         804700  
  31         846  
7 31     31   234 use IO::Handle;
  31         83  
  31         21707  
8              
9             sub new {
10 35     35 0 91 my $class = shift;
11 35         128 my %args = @_;
12 35         140 my $filename = File::Temp::tmpnam();
13              
14 35   50     16595 my $init = Storable::dclone($args{init} || +{});
15              
16             my $self = bless {
17             callback_on_open => $args{cb},
18 35         693 filename => $filename,
19             lock => 0,
20             pid => $$,
21             ppid => $$,
22             }, $class;
23 35         126 $self->open();
24              
25             # initialize
26 35 50       177 Storable::nstore_fd($init, $self->{fh}) or die "Cannot write initialize data to $filename";
27              
28 35         4302 return $self;
29             }
30              
31             sub open {
32 54     54 0 154 my $self = shift;
33 54 50       355 if (my $cb = $self->{callback_on_open}) {
34 54         412 $cb->($self);
35             }
36 54 50       17385 sysopen my $fh, $self->{filename}, O_RDWR|O_CREAT or die $!;
37 54         1346 $fh->autoflush(1);
38 54         5806 $self->{fh} = $fh;
39             }
40              
41             sub close {
42 19     19 0 258 my $self = shift;
43 19         1621 close $self->{fh};
44 19         1410 undef $self->{fh};
45             }
46              
47             sub get {
48 1522     1522 0 3850 my ($self, $key) = @_;
49 1522         3776 $self->_reopen_if_needed;
50 1522 50       8020 seek $self->{fh}, 0, SEEK_SET or die $!;
51 1522         6825 Storable::fd_retrieve($self->{fh})->{$key};
52             }
53              
54             sub set {
55 495     495 0 1196 my ($self, $key, $val) = @_;
56              
57 495         1430 $self->_reopen_if_needed;
58              
59 495 50       2576 seek $self->{fh}, 0, SEEK_SET or die $!;
60 495         1742 my $dat = Storable::fd_retrieve($self->{fh});
61 495         41825 $dat->{$key} = $val;
62              
63 495         24490347 truncate $self->{fh}, 0;
64 495 50       3504 seek $self->{fh}, 0, SEEK_SET or die $!;
65 495 50       4033 Storable::nstore_fd($dat => $self->{fh}) or die "Cannot store data to $self->{filename}";
66             }
67              
68             sub get_lock {
69 2089     2089 0 4298 my ($self, ) = @_;
70 2089         8923 Test::SharedFork::Store::Locker->new($self);
71             }
72              
73             sub _reopen_if_needed {
74 4106     4106   6483 my $self = shift;
75 4106 100       18804 if ($self->{pid} != $$) { # forked, and I'm just a child.
76 19         823 $self->{pid} = $$;
77 19 50       900 if ($self->{lock} > 0) { # unlock! I'm not owner!
78 0 0       0 flock $self->{fh}, LOCK_UN or die $!;
79 0         0 $self->{lock} = 0;
80             }
81 19         787 $self->close();
82 19         2120 $self->open();
83             }
84             }
85              
86             sub DESTROY {
87 0     0   0 my $self = shift;
88 0 0       0 if ($self->{ppid} eq $$) { # cleanup method only run on original process.
89 0         0 unlink $self->{filename};
90             }
91             }
92              
93             package # hide from pause
94             Test::SharedFork::Store::Locker;
95              
96 31     31   187 use Fcntl ':flock';
  31         61  
  31         9022  
97              
98             sub new {
99 2089     2089   4632 my ($class, $store) = @_;
100              
101 2089         6700 $store->_reopen_if_needed;
102              
103 2089 100       6838 if ($store->{lock}++ == 0) {
104 640 50       18099164 flock $store->{fh}, LOCK_EX or die $!;
105             }
106              
107 2089         14593 bless { store => $store }, $class;
108             }
109              
110             sub DESTROY {
111 2089     2089   229357 my ($self) = @_;
112              
113 2089         5011 $self->{store}->{lock}--;
114 2089 100       16278 if ($self->{store}->{lock} == 0) {
115 640 50       11575 flock $self->{store}->{fh}, LOCK_UN or die $!;
116             }
117             }
118              
119             1;