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