File Coverage

blib/lib/Proc/Hevy.pm
Criterion Covered Total %
statement 70 72 97.2
branch 34 38 89.4
condition 14 21 66.6
subroutine 9 9 100.0
pod 1 1 100.0
total 128 141 90.7


line stmt bran cond sub pod time code
1             package Proc::Hevy;
2              
3 25     25   387784 use strict;
  25         62  
  25         952  
4 25     25   141 use warnings;
  25         45  
  25         723  
5              
6 25     25   128 use Carp;
  25         45  
  25         1805  
7 25     25   23459 use Errno qw( EWOULDBLOCK );
  25         34698  
  25         3045  
8 25     25   22549 use IO::Pipe;
  25         260994  
  25         926  
9 25     25   25707 use IO::Select;
  25         42022  
  25         1478  
10 25     25   14137 use Proc::Hevy::Reader;
  25         83  
  25         839  
11 25     25   14766 use Proc::Hevy::Writer;
  25         69  
  25         21685  
12              
13              
14             sub exec {
15 103     103 1 133857 my ( $class, @args ) = @_;
16              
17 103 100       3206 confess 'Odd number of parameters'
18             unless @args % 2 == 0;
19 97         562 my %args = @args;
20              
21 97 100       3097 confess 'command: Required parameter not defined'
22             unless defined $args{command};
23              
24 91 100       1250 if( ref( $args{command} ) =~ /^(?:CODE)?\z/ ) {
    100          
25 70         260 $args{command} = [ $args{command} ];
26             }
27             elsif( ref( $args{command} ) ne 'ARRAY' ) {
28 6         1902 confess 'command: Must be an ARRAY reference';
29             }
30              
31 85 100 66     2002 ref( $args{stdin} ) =~ /^(?:ARRAY|CODE|GLOB)\z/
      100        
32             or confess 'stdin: Must be one of ARRAY, CODE or GLOB reference'
33             if exists $args{stdin} and ref $args{stdin};
34              
35 79 100 66     2177 ref( $args{stdout} ) =~ /^(?:ARRAY|CODE|GLOB|SCALAR)\z/
36             or confess 'stdout: Must be one of ARRAY, CODE, GLOB or SCALAR reference'
37             if exists $args{stdout};
38              
39 73 100 66     1637 ref( $args{stderr} ) =~ /^(?:ARRAY|CODE|GLOB|SCALAR)\z/
40             or confess 'stderr: Must be one of ARRAY, CODE, GLOB or SCALAR reference'
41             if exists $args{stderr};
42              
43 67 100 33     292 ref( $args{parent} ) eq 'CODE'
44             or confess 'parent: Must be a CODE reference'
45             if defined $args{parent};
46              
47 67 100 33     265 ref( $args{child} ) eq 'CODE'
48             or confess 'child: Must be a CODE reference'
49             if defined $args{child};
50              
51 67 100       379 $args{priority} = 0
52             unless defined $args{priority};
53              
54 67         1123 my $std_i = Proc::Hevy::Writer->new( stdin => $args{stdin} );
55 67         970 my $std_o = Proc::Hevy::Reader->new( stdout => $args{stdout} );
56 67         419 my $std_e = Proc::Hevy::Reader->new( stderr => $args{stderr} );
57              
58             # fork
59 67         85578 my $pid = fork;
60 67 50       3345 confess "fork: $!\n"
61             unless defined $pid;
62              
63 67 100       2212 if( $pid == 0 ) {
64             # child
65              
66             # run callback, if needed
67 20 100       1984 $args{child}->( getppid )
68             if defined $args{child};
69              
70             # set filehandles
71 20         2239 $std_i->child( \*STDIN, 0 );
72 20         2385 $std_o->child( \*STDOUT, 1 );
73 20         622 $std_e->child( \*STDERR, 2 );
74              
75             # set process priority, if needed
76 20 100       982 if( $args{priority} ) {
77 2         114 my $priority = getpriority( 0, $$ );
78 2 50       16 defined $priority
79             or die "getpriority(): $$: $!\n";
80              
81 2         6 $priority += $args{priority};
82              
83 2 50       135 setpriority( 0, $$, $priority )
84             or die "setpriority(): $$: $!\n";
85             }
86              
87             # exec
88 20 100       436 if( ref( $args{command}->[0] ) eq 'CODE' ) {
89 18         97 my $sub = shift @{ $args{command} };
  18         244  
90 18         128 $sub->( @{ $args{command} } );
  18         661  
91 13         9998 exit 0;
92             }
93              
94 2         7 exec @{ $args{command} };
  2         0  
95 0         0 warn "exec: $args{command}->[0]: $!\n";
96 0         0 exit 1;
97             }
98              
99             # parent
100              
101             # run callback, if needed
102 47 100       1040 $args{parent}->( $pid )
103             if defined $args{parent};
104              
105             # set filehandles
106 47         6323 my ( $select_w, $select_r ) = ( IO::Select->new, IO::Select->new );
107              
108 47         4905 my %handles = (
109             $std_i->parent( $select_w ),
110             $std_o->parent( $select_r ),
111             $std_e->parent( $select_r ),
112             );
113              
114             # loop
115 47   100     841 while( $select_r->count or $select_w->count ) {
116 108         2028 my ( $readers, $writers ) = IO::Select->select( $select_r, $select_w );
117              
118             $handles{$_}->read
119 108         17322160 for @$readers;
120              
121             $handles{$_}->write
122 108         2248 for @$writers;
123             }
124              
125             # use waitpid() to avoid signal handlers
126 47         3640125 my $rc = waitpid $pid, 0;
127 47 50       309 confess "waitpid: $!"
128             if $rc == -1;
129              
130 47         4909 return $?;
131             }
132              
133              
134             1
135             __END__