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   139 use strict;
  31         47  
  31         1115  
3 31     31   123 use warnings;
  31         47  
  31         797  
4 31     31   116 use Storable ();
  31         40  
  31         702  
5 31     31   120 use Fcntl ':seek', ':DEFAULT', ':flock';
  31         40  
  31         12777  
6 31     31   24044 use File::Temp ();
  31         606787  
  31         810  
7 31     31   227 use IO::Handle;
  31         67  
  31         19409  
8              
9             sub new {
10 35     35 0 85 my $class = shift;
11 35         105 my %args = @_;
12 35         143 my $filename = File::Temp::tmpnam();
13              
14 35   50     14731 my $init = Storable::dclone($args{init} || +{});
15              
16 35         913 my $self = bless {
17             callback_on_open => $args{cb},
18             filename => $filename,
19             lock => 0,
20             pid => $$,
21             ppid => $$,
22             }, $class;
23 35         118 $self->open();
24              
25             # initialize
26 35 50       135 Storable::nstore_fd($init, $self->{fh}) or die "Cannot write initialize data to $filename";
27              
28 35         3858 return $self;
29             }
30              
31             sub open {
32 54     54 0 140 my $self = shift;
33 54 50       347 if (my $cb = $self->{callback_on_open}) {
34 54         222 $cb->($self);
35             }
36 54 50       5707 sysopen my $fh, $self->{filename}, O_RDWR|O_CREAT or die $!;
37 54         952 $fh->autoflush(1);
38 54         4344 $self->{fh} = $fh;
39             }
40              
41             sub close {
42 19     19 0 113 my $self = shift;
43 19         786 close $self->{fh};
44 19         256 undef $self->{fh};
45             }
46              
47             sub get {
48 1522     1522 0 2129 my ($self, $key) = @_;
49 1522         3374 $self->_reopen_if_needed;
50 1522 50       6093 seek $self->{fh}, 0, SEEK_SET or die $!;
51 1522         5026 Storable::fd_retrieve($self->{fh})->{$key};
52             }
53              
54             sub set {
55 495     495 0 800 my ($self, $key, $val) = @_;
56              
57 495         1028 $self->_reopen_if_needed;
58              
59 495 50       2042 seek $self->{fh}, 0, SEEK_SET or die $!;
60 495         1208 my $dat = Storable::fd_retrieve($self->{fh});
61 495         30936 $dat->{$key} = $val;
62              
63 495         1131382 truncate $self->{fh}, 0;
64 495 50       2604 seek $self->{fh}, 0, SEEK_SET or die $!;
65 495 50       2473 Storable::nstore_fd($dat => $self->{fh}) or die "Cannot store data to $self->{filename}";
66             }
67              
68             sub get_lock {
69 2089     2089 0 2555 my ($self, ) = @_;
70 2089         5823 Test::SharedFork::Store::Locker->new($self);
71             }
72              
73             sub _reopen_if_needed {
74 4106     4106   3914 my $self = shift;
75 4106 100       13738 if ($self->{pid} != $$) { # forked, and I'm just a child.
76 19         536 $self->{pid} = $$;
77 19 50       930 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         424 $self->close();
82 19         1482 $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   188 use Fcntl ':flock';
  31         64  
  31         8324  
97              
98             sub new {
99 2089     2089   3274 my ($class, $store) = @_;
100              
101 2089         3954 $store->_reopen_if_needed;
102              
103 2089 100       5476 if ($store->{lock}++ == 0) {
104 640 50       935210 flock $store->{fh}, LOCK_EX or die $!;
105             }
106              
107 2089         13094 bless { store => $store }, $class;
108             }
109              
110             sub DESTROY {
111 2089     2089   166287 my ($self) = @_;
112              
113 2089         3699 $self->{store}->{lock}--;
114 2089 100       11555 if ($self->{store}->{lock} == 0) {
115 640 50       10252 flock $self->{store}->{fh}, LOCK_UN or die $!;
116             }
117             }
118              
119             1;