File Coverage

blib/lib/Doit/Fork.pm
Criterion Covered Total %
statement 85 96 88.5
branch 16 24 66.6
condition n/a
subroutine 14 16 87.5
pod 0 3 0.0
total 115 139 82.7


line stmt bran cond sub pod time code
1             # -*- perl -*-
2              
3             #
4             # Author: Slaven Rezic
5             #
6             # Copyright (C) 2017,2023,2024 Slaven Rezic. All rights reserved.
7             # This package is free software; you can redistribute it and/or
8             # modify it under the same terms as Perl itself.
9             #
10             # Mail: slaven@rezic.de
11             # WWW: http://www.rezic.de/eserte/
12             #
13              
14             package Doit::Fork;
15              
16 2     2   10 use Doit;
  2         2  
  2         12  
17              
18 2     2   6 use strict;
  2         2  
  2         26  
19 2     2   4 use warnings;
  2         2  
  2         78  
20             our $VERSION = '0.03';
21              
22 2     2   6 use vars '@ISA'; @ISA = ('Doit::_AnyRPCImpl');
  2         2  
  2         94  
23              
24 2     2   6 use Doit::Log;
  2         2  
  2         1042  
25              
26             our @last_exits;
27             our $keep_last_exits; $keep_last_exits = 10 if !defined $keep_last_exits;
28              
29 2     2 0 14 sub new { bless {}, shift }
30 2     2 0 6 sub functions { qw() }
31              
32             sub do_connect {
33 2     2 0 6 my($class, %opts) = @_;
34              
35 2         4 my $dry_run = delete $opts{dry_run};
36 2         2 my $debug = delete $opts{debug};
37 2 50       4 die "Unhandled options: " . join(" ", %opts) if %opts;
38              
39 2         2 my $self = bless { }, $class;
40              
41 2         2 my $d;
42 2 50       6 if ($debug) {
43             $d = sub ($) {
44 0     0   0 Doit::Log::info("PARENT: $_[0]");
45 0         0 };
46             } else {
47 2     3   8 $d = sub ($) { };
48             }
49 2         6 $self->{d} = $d;
50              
51 2         1220 require IO::Pipe;
52 2         2008 my $pipe_to_fork = IO::Pipe->new;
53 2         188 my $pipe_from_fork = IO::Pipe->new;
54 2         4704 my $worker_pid = fork;
55 2 50       243 if (!defined $worker_pid) {
    100          
56 0         0 error "fork failed: $!";
57             } elsif ($worker_pid == 0) {
58 1         16 my $d = do {
59 1 50       77 local @ARGV = $dry_run ? '--dry-run' : ();
60 1         154 Doit->init;
61             };
62 1         28 $pipe_to_fork->reader;
63 1         139 $pipe_from_fork->writer;
64 1         62 $pipe_from_fork->autoflush(1);
65 1         103 Doit::RPC::PipeServer->new($d, $pipe_to_fork, $pipe_from_fork, debug => $debug)->run;
66 1         56 CORE::exit(0);
67             }
68              
69 1         113 $d->("Forked worker $worker_pid...");
70              
71 1         58 $pipe_to_fork->writer;
72 1         333 $pipe_from_fork->reader;
73 1         107 $self->{rpc} = Doit::RPC::Client->new($pipe_from_fork, $pipe_to_fork, label => "fork:", debug => $debug);
74 1         20 $self->{pid} = $worker_pid;
75              
76 1         29 $self;
77             }
78              
79             sub DESTROY {
80 4     4   8 my $self = shift;
81             # Note: if new() is called without followed by do_connect(), then no {pid} is set
82 4 100       16 if (defined $self->{pid}) {
83 1         7 $self->{d}->("About to destroy fork with pid $self->{pid}...");
84             }
85 4         55 delete $self->{rpc};
86 4 100       423 if (defined $self->{pid}) {
87 1         6 $self->{d}->(" reap child process");
88 1         1102073 waitpid $self->{pid}, 0;
89 1         48 my %exit_res = Doit::_analyze_dollar_questionmark();
90 1         9 $exit_res{pid} = $self->{pid};
91 1         6 push @last_exits, \%exit_res;
92 1 50       7 if (defined $keep_last_exits) {
93 1         32 while (@last_exits > $keep_last_exits) {
94 0         0 shift @last_exits;
95             }
96             }
97             }
98             }
99              
100             {
101             package Doit::RPC::PipeServer;
102 2     2   10 use vars '@ISA'; @ISA = ('Doit::RPC');
  2         4  
  2         778  
103              
104             sub new {
105 1     1   8 my($class, $runner, $pipe_to_server, $pipe_from_server, %options) = @_;
106              
107 1         2 my $debug = delete $options{debug};
108 1 50       13 die "Unhandled options: " . join(" ", %options) if %options;
109              
110 1         14 bless {
111             runner => $runner,
112             pipe_to_server => $pipe_to_server,
113             pipe_from_server => $pipe_from_server,
114             debug => $debug,
115             }, $class;
116             }
117              
118             sub run {
119 1     1   12 my($self) = @_;
120              
121 1         1 my $d;
122 1 50       7 if ($self->{debug}) {
123             $d = sub ($) {
124 0     0   0 Doit::Log::info("WORKER: $_[0]");
125 0         0 };
126             } else {
127 1     15   15 $d = sub ($) { };
128             }
129              
130 1         34 $d->("Start worker ($$)...");
131 1         2 my $pipe_to_server = $self->{pipe_to_server};
132 1         9 my $pipe_from_server = $self->{pipe_from_server};
133              
134 1         9 $self->{infh} = $pipe_to_server;
135 1         12 $self->{outfh} = $pipe_from_server;
136 1         3 while () {
137 5         40 $d->(" waiting for line from comm");
138 5         61 my($context, @data) = $self->receive_data;
139 5 100       273 if (!defined $context) {
    50          
140 1         9 $d->(" got eof");
141 1         12 $pipe_to_server->close;
142 1         133 $pipe_from_server->close;
143 1         33 return;
144             } elsif ($data[0] =~ m{^exit$}) {
145 0         0 $d->(" got exit command");
146 0         0 $self->send_data('r', 'bye-bye');
147 0         0 $pipe_to_server->close;
148 0         0 $pipe_from_server->close;
149 0         0 return;
150             }
151 4         23 $d->(" calling method $data[0]");
152 4         89 my($rettype, @ret) = $self->{runner}->call_wrapped_method($context, @data);
153 4         24 $d->(" sending result back");
154 4         51 $self->send_data($rettype, @ret);
155             }
156             }
157             }
158              
159             1;
160              
161             __END__