File Coverage

blib/lib/Async.pm
Criterion Covered Total %
statement 55 76 72.3
branch 20 26 76.9
condition 0 3 0.0
subroutine 8 13 61.5
pod 0 4 0.0
total 83 122 68.0


line stmt bran cond sub pod time code
1 9     9   468317 use 5.006; use strict; no warnings;
  9     9   83  
  9     9   36  
  9         11  
  9         221  
  9         39  
  9         25  
  9         7227  
2              
3             package Async;
4              
5             our $VERSION = '0.13';
6              
7             our $ERROR;
8              
9             sub new {
10 19     19 0 3656 my ( $class, $task ) = ( shift, @_ );
11              
12 19         35 my ( $r, $w );
13 19 100       600 unless ( pipe $r, $w ) {
14 1         18 $ERROR = "Couldn't make pipe: $!";
15 1         3 return;
16             }
17              
18 18         10128 my $pid = fork;
19 18 100       653 unless ( defined $pid ) {
20 1         13 $ERROR = "Couldn't fork: $!";
21 1         24 return;
22             }
23              
24 17 100       426 if ( $pid ) { # parent
25 16         526 close $w;
26 16         1276 my $self = {
27             TASK => $task,
28             PID => $pid,
29             PPID => $$,
30             PIPE => $r,
31             FD => fileno $r,
32             DATA => '',
33             };
34 16         1711 bless $self, $class;
35             } else { # child
36 1         128 close $r;
37 1         140 my $result = $task->();
38 1         121 print $w $result;
39 1         218 exit 0;
40             }
41             }
42              
43             # return true iff async process is complete
44             # with true `$force' argmuent, wait until process is complete before returning
45             sub ready {
46 10     10 0 504799 my ( $self, $force ) = ( shift, @_ );
47              
48 10         29 my $timeout;
49 10 100       53 $timeout = 0 unless $force;
50              
51 10 100       101 return 1 if $self->{'FINISHED'};
52              
53 7         39 my $fdset = '';
54 7         48 vec( $fdset, $self->{'FD'}, 1 ) = 1;
55              
56 7         196435 while ( select $fdset, undef, undef, $timeout ) {
57 9         32 my $buf;
58 9         228 my $nr = read $self->{'PIPE'}, $buf, 8192;
59 9 100       111 if ( $nr ) {
    100          
60 4         47 $self->{'DATA'} .= $buf;
61             } elsif ( defined $nr ) { # EOF
62 3         12 $self->{'FINISHED'} = 1;
63 3         16 return 1;
64             } else {
65 2         48 $self->{'ERROR'} = "Read error: $!";
66 2         83 $self->{'FINISHED'} = 1;
67 2         71 return 1;
68             }
69             }
70              
71 2         16 return 0;
72             }
73              
74             # Return error message if an error occurred
75             # Return false if no error occurred
76 3     3 0 713 sub error { $_[0]{'ERROR'} }
77              
78             # Return resulting data if async process is complete
79             # return undef if it is incopmplete
80             # a true $force argument waits for the process to complete before returning
81             sub result {
82 4     4 0 2667 my ( $self, $force ) = ( shift, @_ );
83 4 100       51 if ( $self->{'FINISHED'} ) {
    100          
84 1         15 $self->{'DATA'};
85             } elsif ( $force ) {
86 2         19 $self->ready( $force );
87 2         19 $self->{'DATA'};
88             } else {
89 1         15 return;
90             }
91             }
92              
93             sub DESTROY {
94 16     16   11869 my $self = shift;
95 16 100       1109 return if $self->{'PPID'} != $$; # created in a different process
96 15         33 my $pid = $self->{'PID'};
97 15         186 local ( $., $@, $!, $^E, $? );
98 15         427 kill 9, $pid; # I don't care.
99 15         12266 waitpid $pid, 0;
100             }
101              
102             package AsyncTimeout;
103             our @ISA = 'Async';
104              
105             our $VERSION = '0.13';
106              
107             sub new {
108 0     0     my ( $class, $task, $timeout, $msg ) = ( shift, @_ );
109 0 0         $msg = "Timed out\n" unless defined $msg;
110             my $newtask = sub {
111 0     0     local $SIG{'ALRM'} = sub { die "TIMEOUT\n" };
  0            
112 0           alarm $timeout;
113 0           my $s = eval { $task->() };
  0            
114 0 0 0       return $msg if not defined $s and $@ eq "TIMEOUT\n";
115 0           return $s;
116 0           };
117 0           $class->SUPER::new( $newtask );
118             }
119              
120             package AsyncData;
121             our @ISA = 'Async';
122              
123             our $VERSION = '0.13';
124              
125             sub new {
126 0     0     require Storable;
127 0           my ( $class, $task ) = ( shift, @_ );
128             my $newtask = sub {
129 0     0     my $v = $task->();
130 0           return Storable::freeze( $v );
131 0           };
132 0           $class->SUPER::new( $newtask );
133             }
134              
135             sub result {
136 0     0     require Storable;
137 0           my $self = shift;
138 0           my $rc = $self->SUPER::result( @_ );
139 0 0         return defined $rc ? Storable::thaw( $rc ) : $rc;
140             }
141              
142             1;
143              
144             __END__