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   164 use strict;
  31         71  
  31         898  
3 31     31   157 use warnings;
  31         60  
  31         851  
4 31     31   241 use Storable ();
  31         73  
  31         619  
5 31     31   143 use Fcntl ':seek', ':DEFAULT', ':flock';
  31         52  
  31         17945  
6 31     31   34988 use File::Temp ();
  31         779345  
  31         896  
7 31     31   281 use IO::Handle;
  31         76  
  31         21358  
8              
9             sub new {
10 35     35 0 109 my $class = shift;
11 35         207 my %args = @_;
12 35         168 my $filename = File::Temp::tmpnam();
13              
14 35   50     20320 my $init = Storable::dclone($args{init} || +{});
15              
16             my $self = bless {
17             callback_on_open => $args{cb},
18 35         758 filename => $filename,
19             lock => 0,
20             pid => $$,
21             ppid => $$,
22             }, $class;
23 35         143 $self->open();
24              
25             # initialize
26 35 50       181 Storable::nstore_fd($init, $self->{fh}) or die "Cannot write initialize data to $filename";
27              
28 35         191455 return $self;
29             }
30              
31             sub open {
32 54     54 0 149 my $self = shift;
33 54 50       396 if (my $cb = $self->{callback_on_open}) {
34 54         592 $cb->($self);
35             }
36 54 50       9132 sysopen my $fh, $self->{filename}, O_RDWR|O_CREAT or die $!;
37 54         1379 $fh->autoflush(1);
38 54         7040 $self->{fh} = $fh;
39             }
40              
41             sub close {
42 19     19 0 235 my $self = shift;
43 19         1928 close $self->{fh};
44 19         1218 undef $self->{fh};
45             }
46              
47             sub get {
48 1522     1522 0 4520 my ($self, $key) = @_;
49 1522         4095 $self->_reopen_if_needed;
50 1522 50       9570 seek $self->{fh}, 0, SEEK_SET or die $!;
51 1522         8603 Storable::fd_retrieve($self->{fh})->{$key};
52             }
53              
54             sub set {
55 495     495 0 2097 my ($self, $key, $val) = @_;
56              
57 495         2937 $self->_reopen_if_needed;
58              
59 495 50       3179 seek $self->{fh}, 0, SEEK_SET or die $!;
60 495         2002 my $dat = Storable::fd_retrieve($self->{fh});
61 495         47393 $dat->{$key} = $val;
62              
63 495         43994651 truncate $self->{fh}, 0;
64 495 50       3825 seek $self->{fh}, 0, SEEK_SET or die $!;
65 495 50       4483 Storable::nstore_fd($dat => $self->{fh}) or die "Cannot store data to $self->{filename}";
66             }
67              
68             sub get_lock {
69 2089     2089 0 4405 my ($self, ) = @_;
70 2089         9642 Test::SharedFork::Store::Locker->new($self);
71             }
72              
73             sub _reopen_if_needed {
74 4106     4106   7305 my $self = shift;
75 4106 100       22505 if ($self->{pid} != $$) { # forked, and I'm just a child.
76 19         790 $self->{pid} = $$;
77 19 50       1058 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         683 $self->close();
82 19         2004 $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   214 use Fcntl ':flock';
  31         62  
  31         9570  
97              
98             sub new {
99 2089     2089   5542 my ($class, $store) = @_;
100              
101 2089         7376 $store->_reopen_if_needed;
102              
103 2089 100       8281 if ($store->{lock}++ == 0) {
104 640 50       15874100 flock $store->{fh}, LOCK_EX or die $!;
105             }
106              
107 2089         17425 bless { store => $store }, $class;
108             }
109              
110             sub DESTROY {
111 2089     2089   259232 my ($self) = @_;
112              
113 2089         5352 $self->{store}->{lock}--;
114 2089 100       17903 if ($self->{store}->{lock} == 0) {
115 640 50       12597 flock $self->{store}->{fh}, LOCK_UN or die $!;
116             }
117             }
118              
119             1;