File Coverage

blib/lib/BGS.pm
Criterion Covered Total %
statement 95 107 88.7
branch 21 34 61.7
condition 3 6 50.0
subroutine 14 14 100.0
pod 4 4 100.0
total 137 165 83.0


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