File Coverage

blib/lib/BGS.pm
Criterion Covered Total %
statement 90 104 86.5
branch 23 38 60.5
condition 7 15 46.6
subroutine 13 13 100.0
pod 4 4 100.0
total 137 174 78.7


line stmt bran cond sub pod time code
1             package BGS;
2              
3 10     10   201215 use strict;
  10         25  
  10         329  
4 10     10   60 use warnings;
  10         23  
  10         311  
5              
6 10     10   58 use Exporter;
  10         19  
  10         871  
7             our @ISA = qw(Exporter);
8             our @EXPORT = qw(bgs_call bgs_back bgs_wait bgs_break);
9              
10             our $VERSION = '0.12';
11              
12 10     10   3863 use IO::Select;
  10         16070  
  10         478  
13 10     10   69 use Scalar::Util qw(refaddr);
  10         17  
  10         560  
14 10     10   4639 use Storable qw(freeze thaw);
  10         24266  
  10         682  
15 10     10   2969 use POSIX ":sys_wait_h";
  10         46515  
  10         53  
16              
17              
18             our $limit = 0;
19              
20              
21             my $sel = IO::Select->new();
22              
23             my %fh2data = ();
24             my %vpid2data = ();
25             my @to_call = ();
26              
27              
28              
29             sub _call {
30 20     20   59 my ($data) = @_;
31              
32 20         51 my $sub = delete $$data{sub};
33              
34 20 50       559 pipe my $from_kid_fh, my $to_parent_fh or die "pipe: $!";
35              
36 20         13821 my $kid_pid = fork;
37 20 50       699 defined $kid_pid or die "Can't fork: $!";
38              
39 20 100       373 if ($kid_pid) {
40 13         489 $sel->add($from_kid_fh);
41              
42 13         1869 my $vpid = $$data{vpid};
43              
44 13         153 $$data{fh} = $from_kid_fh;
45 13         83 $$data{pid} = $kid_pid;
46              
47 13         153 $fh2data{$from_kid_fh} = $data;
48 13         438 $vpid2data{$vpid} = $data;
49              
50             } else {
51 7         455 %fh2data = ();
52 7         394 %vpid2data = ();
53 7         76 @to_call = ();
54              
55 7         227 binmode $to_parent_fh;
56 7         185 print $to_parent_fh freeze \ scalar $sub->();
57 7         2184 close $to_parent_fh;
58 7         1742 exit;
59             }
60              
61             }
62              
63              
64             sub _bgs_call {
65 20     20   40 my ($sub, $callback) = @_;
66              
67 20         114 my $data = { sub => $sub };
68 20         129 my $vpid = $$data{vpid} = refaddr $data;
69              
70 20 100       106 $$data{callback} = $callback if $callback;
71              
72 20 50 33     101 if ($limit > 0 and keys %fh2data >= $limit) {
73 0         0 push @to_call, $data;
74             } else {
75 20         71 _call($data);
76             }
77              
78 13         112 return $data;
79             }
80              
81             sub bgs_call(&$) {
82 14     14 1 39 my ($sub, $callback) = @_;
83              
84 14         49 my $data = _bgs_call($sub, $callback);
85              
86 9         328 return $$data{vpid};
87             }
88              
89 5     5 1 262 sub bgs_back(&) { shift }
90              
91              
92             sub bgs_wait(;$) {
93 7     7 1 47 my ($waited) = @_;
94              
95 7 50 66     146 if ($waited and not exists $vpid2data{$waited} and not grep { $$_{vpid} eq $waited } @to_call) {
  0   33     0  
96 0         0 return;
97             }
98              
99 7         255 local $SIG{PIPE} = "IGNORE";
100 7         29 my $buf;
101 7         24 my $blksize = 1024;
102 7         75 while ($sel->count()) {
103 17         178 foreach my $fh ($sel->can_read()) {
104 19         1543 my $data = $fh2data{$fh};
105 19         172 my $len = sysread $fh, $buf, $blksize;
106 19 100       99 if ($len) {
    50          
107 9         40 push @{$$data{from_kid}}, $buf;
  9         161  
108             } elsif (defined $len) {
109 10         59 $sel->remove($fh);
110 10 50       831 close $fh or warn "Kid is existed: $?";
111              
112 10         41 delete $$data{fh};
113 10         27 my $pid = delete $$data{pid};
114 10         43 my $callback = delete $$data{callback};
115            
116 10 100       48 unless ($$data{break}) {
117 9 50 33     85 if (exists $$data{from_kid} and my $r = eval { thaw(join "", @{delete $$data{from_kid}}) }) {
  9         75  
  9         112  
118 9 100       360 if ($callback) {
119 6         30 $callback->($$r);
120             } else {
121 3         17 $$data{result} = $$r;
122             }
123             } else {
124 0 0       0 if ($callback) {
125 0         0 $callback->();
126             } else {
127 0         0 $$data{result} = undef;
128             }
129             }
130             }
131              
132 9         3045 my $vpid = $$data{vpid};
133 9         108 delete $fh2data{$fh};
134 9         33 delete $vpid2data{$vpid};
135              
136 9         1330947 waitpid($pid, 0);
137              
138 9 50       124 if (my $call = shift @to_call) {
139 0         0 _call($call);
140             }
141              
142 9 100 66     248 if ($waited and $waited == $vpid) {
143 4         80 return;
144             }
145              
146             } else {
147 0         0 die "Can't read '$fh': $!";
148             }
149             }
150             }
151             }
152              
153              
154              
155             sub bgs_break(;$) {
156 1     1 1 8 my ($vpid) = @_;
157 1 50       13 if (defined $vpid) {
158 1         4 my $data = $vpid2data{$vpid};
159 1 50       10 defined $data or return;
160 1 50       12 if (my $pid = $$data{pid}) {
161 1         7 $$data{break} = 1;
162 1         642 kill "TERM", $pid;
163             }
164 1         9 @to_call = grep { $$_{vpid} ne $vpid } @to_call;
  0            
165             } else {
166 0           foreach my $data (values %vpid2data) {
167 0 0         if (my $pid = $$data{pid}) {
168 0           $$data{break} = 1;
169 0           kill "TERM", $pid;
170             }
171             }
172 0           @to_call = ();
173             }
174             }
175              
176              
177             1;
178              
179              
180             __END__