File Coverage

blib/lib/Proc/Fork/Control.pm
Criterion Covered Total %
statement 16 188 8.5
branch 3 104 2.8
condition 0 19 0.0
subroutine 4 25 16.0
pod 19 19 100.0
total 42 355 11.8


line stmt bran cond sub pod time code
1             package Proc::Fork::Control;
2              
3             #
4             # Copyright (C) 2014 Colin Faber
5             #
6             # This program is free software: you can redistribute it and/or modify
7             # it under the terms of the GNU General Public License as published by
8             # the Free Software Foundation version 2 of the License.
9             #
10             # This program is distributed in the hope that it will be useful,
11             # but WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13             # GNU General Public License for more details.
14             #
15             # You should have received a copy of the GNU General Public License
16             # along with this program. If not, see .
17             #
18             #
19             # Original author: Colin Faber
20             # Original creation date: 10/02/2014
21             # Version: $Id: Control.pm,v 1.5 2015/12/13 01:42:05 cfaber Exp $
22             #
23              
24             # Version number - cvs automagically updated.
25             our $VERSION = $1 if('$Revision: 1.5 $' =~ /: ([\d\.]+) \$/);
26              
27 1     1   11784 use strict;
  1         3  
  1         31  
28 1     1   843 use POSIX ('WNOHANG','setsid');
  1         8220  
  1         13  
29 1     1   2318 use Time::HiRes 'usleep';
  1         1562  
  1         6  
30              
31             require Exporter;
32              
33             # Exported routines
34             our @ISA = ('Exporter');
35             our @EXPORT = qw(
36             cfork
37             cfork_wait
38             cfork_wait_pid
39             cfork_init
40             cfork_exit
41             cfork_exit_code
42             cfork_maxchildren
43             cfork_errstr
44             cfork_is_child
45             cfork_has_children
46             cfork_nonblocking
47             cfork_daemonize
48             cfork_sleep
49             cfork_usleep
50             cfork_ssleep
51             cfork_active_children
52             cfork_kill_children
53             cfork_list_children
54             cfork_child_dob
55             );
56              
57             # Defaults
58             &cfork_init;
59              
60             require 5.008;
61              
62             $Proc::Fork::Control::VERSION = '$Revision: 1.5 $';
63              
64             =head1 NAME
65              
66             Proc::Fork::Control
67              
68             =head1 DESCRIPTION
69              
70             Proc::Fork::Control is a simple to use library which functions much the same way
71             as Proc::Fork. That said, Proc::Fork is not used, as fork() is accessed directly.
72              
73             Proc::Fork::Control allows you to manage forks, control number of children
74             allowed, monitor children, control blocking and nonblocking states, etc.
75              
76             =head1 SYNOPSIS
77              
78             #!/usr/bin/perl
79             use Proc::Fork::Control;
80             use Fcntl ':flock';
81              
82             # Initialize the system allowing 25 forks per cfork() level
83             cfork_init(25);
84              
85             for(my $i = 0; $i < 50; $i++){
86             # Fork this if possible, if all avaliable fork slots are full
87             # block until one becomes avaliable.
88             cfork(sub {
89             # Initialize for children
90             cfork_init(2);
91              
92             for('A' .. 'Z'){
93             cfork(sub {
94             # Lock STDOUT for writing.
95             flock(STDOUT, &LOCK_EX);
96              
97             # Print out a string.
98             print STDOUT "Fork: $i: $_\n";
99            
100             # Unlock STDOUT.
101             flock(STDOUT, &LOCK_UN);
102              
103             cfork_exit();
104             });
105             }
106              
107             # Wait for sub children to exit
108             cfork_wait()
109              
110             });
111             }
112              
113             # Wait until all forks have finished.
114             cfork_wait();
115              
116             =head1 METHODS
117              
118             Note - because of the nature of forking within perl. I've against objectifying this library. Rather it uses direct function calls which are exported to the global namespace Below is a list of these calls and how to access them.
119              
120             =head2 cfork(code, code, code)
121              
122             Provide managed forking functions.
123              
124             Returns nothing on error and sets the cfork_errstr error handler.
125              
126             if cfork() is called with in an cfork()ed process the calling cfork() process will block until all children with in it die off.
127              
128             =cut
129              
130             sub cfork {
131 0     0 1 0 _errstr();
132 0 0       0 if(!$Proc::Fork::Control::HEAP->{max_children}){
133 0         0 return _errstr("cfork_init() not set");
134             }
135              
136 0 0       0 if(!defined $Proc::Fork::Control::HEAP->{children}){
137 0         0 $Proc::Fork::Control::HEAP->{children} = 0;
138             }
139              
140 0 0       0 if(!defined $Proc::Fork::Control::HEAP->{max_children}){
141 0         0 $Proc::Fork::Control::HEAP->{max_children} = 0;
142             }
143              
144 0         0 my ($i, $delay);
145 0         0 while(1){
146 0         0 my $cl =_cleanup();
147              
148 0 0       0 if($Proc::Fork::Control::HEAP->{children} < $Proc::Fork::Control::HEAP->{max_children}){
149 0         0 last;
150             }
151              
152 0 0       0 if($Proc::Fork::Control::HEAP->{children}){
153 0 0       0 if($cl){
154             # There are still children alive, and we've cleaned up at least 1
155             # child on the last iteration so don't delay at all.
156 0         0 $i = 0;
157 0         0 $delay = 0;
158             } else {
159             # There are still children alive, delay based on the number of interations
160             # Minimum delay time (in micro seconds)
161 0   0     0 $delay ||= 5;
162              
163             # iterator for delay multiplication.
164 0         0 $i++;
165              
166 0         0 $delay = $delay * $i;
167              
168             # Maximum delay value
169 0 0       0 $delay = ($delay > 5000 ? 5000 : $delay);
170              
171             # sleep for a while..
172 0         0 cfork_usleep($delay);
173             }
174             }
175             }
176              
177 0 0       0 if($Proc::Fork::Control::HEAP->{is_child}){
178 0         0 $Proc::Fork::Control::HEAP->{has_children} = 1;
179             }
180              
181 0         0 my $pid = fork;
182 0 0       0 if($pid < 0){
    0          
183 0         0 return _errstr('fork failed: ' . $!);
184             } elsif($pid){
185             # This probably should use CLOCK_MONOTONIC time here, but it's not a big deal.
186 0         0 $Proc::Fork::Control::HEAP->{cidlist}->{$pid} = time();
187 0         0 $Proc::Fork::Control::HEAP->{children}++;
188             } else {
189 0         0 cfork_init();
190 0         0 $Proc::Fork::Control::HEAP->{is_child} = 1;
191 0         0 $SIG{PIPE} = 'IGNORE';
192 0         0 for my $code (@_){
193 0 0       0 if(ref($code) eq 'CODE'){
194 0         0 &{ $code };
  0         0  
195             }
196             }
197              
198 0         0 cfork_exit(2);
199             }
200              
201             # Wait for children to finish (if nonblocking
202 0         0 cfork_wait();
203              
204             # Return our PID for further use.
205 0         0 return $pid;
206             }
207              
208             =head2 cfork_nonblocking(BOOL)
209              
210             Set the cfork() behavior to nonblocking mode if is true, This will result in the fork returning right away rather than waiting for any possible children to die.
211              
212             Also, cfork_nonblocking() should always be turned off after the bit of code you want to run, runs.
213              
214             =item EXAMPLE
215              
216             cfork_nonblocking(0);
217              
218             cfork(sub {
219             do some work;
220             });
221              
222             cfork_nonblocking(1);
223              
224             =cut
225              
226             sub cfork_nonblocking {
227 0     0 1 0 $Proc::Fork::Control::HEAP->{nonblocking} = $_[0];
228             }
229              
230             =head2 cfork_is_child()
231              
232             Return true if called with in a forked enviroment, otherwise return false.
233              
234             =cut
235              
236             sub cfork_is_child {
237 0     0 1 0 return $Proc::Fork::Control::HEAP->{is_child};
238             }
239              
240             =head2 cfork_has_children()
241              
242             Return true if children exist with in a forked enviroment.
243              
244             =cut
245              
246             sub cfork_has_children {
247 0     0 1 0 return $Proc::Fork::Control::HEAP->{has_children};
248             }
249              
250             =head2 cfork_errstr()
251              
252             Return the last error message.
253              
254             =cut
255              
256             sub cfork_errstr {
257 0     0 1 0 my ($err) = @_;
258 0 0       0 $Proc::Fork::Control::errstr = $err if $err;
259 0         0 return $Proc::Fork::Control::errstr;
260             }
261              
262             sub _errstr {
263 0     0   0 my ($err) = @_;
264 0         0 cfork_errstr($err);
265 0         0 return;
266             }
267              
268             =head2 cfork_init(children)
269              
270             Initialize the CHLD reaper with a maximum number of
271              
272             This should be called prior to any cfork() calls
273              
274             =cut
275              
276             sub cfork_init {
277 1     1 1 2 my $ic = $Proc::Fork::Control::HEAP->{is_child};
278              
279 1         5 $Proc::Fork::Control::HEAP = {};
280 1         3 $Proc::Fork::Control::HEAP->{children} = 0;
281 1         3 $Proc::Fork::Control::HEAP->{cidlist} = {};
282 1 50       4 $Proc::Fork::Control::HEAP->{is_child} = ($ic ? 1 : 0);
283              
284 1 50       19 $SIG{CHLD} = \&Proc::Fork::Control::_sigchld if !$ic;
285              
286 1 50       8 if($_[0]){
287 0           $Proc::Fork::Control::HEAP->{max_children} = $_[0];
288             }
289             }
290              
291             =head2 cfork_exit(int)
292              
293             Exit a process cleanly and set an exit code.
294              
295             Normally this can be easily handled with $? however, in some cases $? is not reliably delivered.
296              
297             Once called, drop to END {} block and terminate.
298              
299             =cut
300              
301             sub cfork_exit {
302 0     0 1   my ($exit) = @_;
303 0           $Proc::Fork::Control::HEAP->{exit} = $exit;
304 0           exit($exit);
305             }
306              
307             =head2 cfork_exit_code()
308              
309             Returns the last known cfork_exit() code.
310              
311             =cut
312              
313             sub cfork_exit_code {
314 0     0 1   return $Proc::Fork::Control::HEAP->{exit};
315             }
316              
317             =head2 cfork_maxchildren(int)
318              
319             Set/Reset the maximum number of children allowed.
320              
321             =cut
322              
323             sub cfork_maxchildren {
324 0 0   0 1   $Proc::Fork::Control::HEAP->{max_children} = $_[0] if $_[0];
325             }
326              
327             =head2 cfork_wait()
328              
329             Block until all cfork() children have died off unless cfork_nonblocking() is enabled.
330              
331             =cut
332              
333             sub cfork_wait {
334 0     0 1   my ($to) = @_;
335 0 0         return 1 if $Proc::Fork::Control::HEAP->{nonblocking};
336              
337 0 0         $to = time + $to if $to;
338              
339 0           my ($i, $delay);
340 0           while(1){
341 0           my $cl =_cleanup();
342              
343 0 0 0       if(!$Proc::Fork::Control::HEAP->{children}){
    0          
344 0           last;
345             } elsif($to && time >= $to){
346 0           last;
347             }
348              
349 0 0         if($Proc::Fork::Control::HEAP->{children}){
350 0 0         if($cl){
351             # There are still children alive, and we've cleaned up at least 1
352             # child on the last iteration so don't delay at all.
353 0           $i = 0;
354 0           $delay = 0;
355             } else {
356             # There are still children alive, delay based on the number of interations
357             # Minimum delay time (in micro seconds)
358 0   0       $delay ||= 5;
359              
360             # iterator for delay multiplication.
361 0           $i++;
362              
363 0           $delay = $delay * $i;
364              
365             # Maximum delay value
366 0 0         $delay = ($delay > 5000 ? 5000 : $delay);
367              
368             # sleep for a while..
369 0           cfork_usleep($delay);
370             }
371             }
372             }
373              
374 0           return 1;
375             }
376              
377             =head2 cfork_wait_pid(PID, PID, PID, ..)
378              
379             cfork_wait_pid() functions much like cfork_wait() with the exception that it expects a list of PID's and blocks until those PID's have died off. Like cfork_wait(), cfork_wait_pid() will NOT block if cfork_nonblocking() mode is enabled.
380              
381             =cut
382              
383             sub cfork_wait_pid {
384 0     0 1   my (@PID) = @_;
385              
386 0 0         return 1 if $Proc::Fork::Control::HEAP->{nonblocking};
387              
388 0           my $block;
389 0           while(1){
390 0           for(my $i = 0; $i < @PID; $i++){
391 0 0         if(!kill(undef, $PID[$i])){
392 0           delete $PID[$i];
393             }
394             }
395              
396 0 0         last if !@PID;
397              
398 0           cfork_usleep(5000);
399             }
400              
401 0           return 1;
402             }
403              
404              
405             =head2 cfork_active_children()
406              
407             Return the total number of active children.
408              
409             =cut
410              
411             sub cfork_active_children {
412 0     0 1   _cleanup();
413 0 0         return ($Proc::Fork::Control::HEAP->{children} ? $Proc::Fork::Control::HEAP->{children} : 0);
414             }
415              
416             =head2 cfork_daemonize(BOOL)
417              
418             Daemonize the the calling script.
419              
420             If is true write _ALL_ output to /dev/null.
421              
422             If you have termination handling, i.e. %SIG and END {} block control, cfork_daemonize triggers exit signal 2. So... $? == 4
423              
424             =cut
425              
426             sub cfork_daemonize {
427 0     0 1   my $q = $_[0];
428 0 0         chdir('/') || die "Can't chdir to /: $!\n";
429 0 0         if(!$q){
430 0           open STDIN, '/dev/null' || die "Can't read /dev/null: $!\n";
431 0           open STDOUT, '>/dev/null' || die "Can't write to /dev/null: $!\n";
432 0           open STDERR, '>&STDOUT' || die "Can't dup stdout: $!";
433             }
434              
435 0 0         defined(my $pid = fork) || die "Can't fork: $!\n";
436 0 0         cfork_exit(4) if $pid;
437 0 0         setsid || die "Can't start a new session: $!\n";
438             }
439              
440             =head2 cfork_sleep(int)
441              
442             Provides an alarm safe sleep() wrapper. Beacuse we sleep() with in this, ALRM will be issued with in the fork once the sleep cycle has completed. This function wraps sleep with in a while() block and tests to make sure that the seconds requested for the sleep were slept.
443              
444             =cut
445              
446             sub cfork_sleep {
447 0     0 1   my $sleep = $_[0];
448 0 0         return if $sleep !~ /^\d+$/;
449              
450 0           my $sleeper = 0;
451 0           my $slept = 0;
452              
453 0           while(1){
454 0 0 0       if($sleeper < 0 || $sleep <= 0){
    0          
455 0           last;
456             } elsif(!$sleeper) {
457 0           $sleeper = $sleep;
458             }
459              
460 0           my $remain = sleep( abs($sleeper) );
461              
462 0 0 0       if($remain ne $sleeper && $remain < $sleep){
463 0           $slept += $remain;
464 0           $sleeper = $sleeper - $remain;
465              
466 0           next;
467             } else {
468 0           last;
469             }
470             }
471              
472 0           return $slept;
473             }
474              
475             =head2 cfork_usleep(int)
476              
477             Provides an alarm safe Time::HiRes usleep() wrapper. Beacuse we sleep() with in this, ALRM will be issued with in the fork once the sleep cycle has completed. This function wraps sleep with in a while() block and tests to make sure that the seconds requested for the sleep were slept.
478              
479             This function is only avaliable if Time::HiRes is avaliable otherwise it will simply return nothing at all.
480              
481             =cut
482              
483             sub cfork_usleep {
484 0     0 1   my $sleep = $_[0];
485              
486 0           my $sleeper = 0;
487 0           my $slept = 0;
488              
489 0           while(1){
490 0 0 0       if($sleeper < 0 || $sleep <= 0){
    0          
491 0           last;
492             } elsif(!$sleeper) {
493 0           $sleeper = $sleep;
494             }
495              
496 0           my $remain = usleep( abs($sleeper) );
497              
498 0 0 0       if($remain ne $sleeper && $remain < $sleep){
499 0           $slept += $remain;
500 0           $sleeper = $sleeper - $remain;
501              
502 0           next;
503             } else {
504 0           last;
505             }
506             }
507              
508 0           return $slept;
509             }
510              
511             =head2 cfork_ssleep(int)
512              
513             Preform an cfork_sleep() except rather than using standard sleep() (with interruption handling) use a select() call to sleep. This can be useful in environments where sleep() does not behave correctly, and a select() will block for the desired number of seconds properly.
514              
515             =cut
516              
517             sub cfork_ssleep {
518 0     0 1   $Proc::Fork::Control::HEAP->{select_sleep} = 1;
519 0           my $r = cfork_sleep(@_);
520 0           $Proc::Fork::Control::HEAP->{select_sleep} = 0;
521 0           return $r;
522             }
523              
524             =head2 cfork_kill_children(SIGNAL)
525              
526             Send all children (if any) this .
527              
528             If the argument is omitted kill TERM will be used.
529              
530             =cut
531              
532             sub cfork_kill_children {
533 0     0 1   my $sig = $_[0];
534 0           _cleanup();
535 0 0         if(!$sig){
536 0           $sig = 'TERM';
537             }
538              
539 0 0         if($Proc::Fork::Control::HEAP->{cidlist}){
540 0           kill($sig, keys %{ $Proc::Fork::Control::HEAP->{cidlist} });
  0            
541             }
542             }
543              
544             =head2 cfork_list_children(BOOL)
545              
546             Return a list of PID's currently running under this fork.
547              
548             If BOOL is true a hash will be returned rather than a list.
549              
550             =cut
551              
552             sub cfork_list_children {
553 0     0 1   my ($use_hash) = @_;
554 0           _cleanup();
555              
556 0 0         if(!$Proc::Fork::Control::HEAP->{cidlist}){
557 0           return;
558             }
559              
560 0 0         if($use_hash){
561 0           return (%{ $Proc::Fork::Control::HEAP->{cidlist} });
  0            
562             } else {
563 0           return keys %{ $Proc::Fork::Control::HEAP->{cidlist} };
  0            
564             }
565             }
566              
567             =head2 cfork_child_dob(PID)
568              
569             Return the EPOCH Date of Birth for this childs
570              
571             Returns 0 if no child exists under that PID for this fork.
572              
573             =cut
574              
575             sub cfork_child_dob {
576 0     0 1   my $pid = $_[0];
577 0           _cleanup();
578 0 0         if($Proc::Fork::Control::HEAP->{cidlist}->{$pid}){
579 0           return $Proc::Fork::Control::HEAP->{cidlist}->{$pid};
580             } else {
581 0           return;
582             }
583             }
584              
585             # Child handler
586             sub _sigchld {
587 0     0     my $our;
588 0           while((my $p = waitpid(-1, WNOHANG)) > 0){
589             # Mark the process is done ONLY if it's one of our processes.
590 0 0         if($Proc::Fork::Control::HEAP->{cidlist}->{$p}){
591 0           $Proc::Fork::Control::HEAP->{cidlist}->{$p} = 0;
592 0           $our = 1;
593             }
594             }
595              
596             # self reference only if it's one of our processes.
597 0 0         if($our){
598 0           $SIG{CHLD} = \&Proc::Fork::Control::_sigchld;
599             }
600             }
601              
602              
603             # clean up lists - thanks to gmargo@perlmonks for this idea.
604             sub _cleanup {
605 0     0     my $i = 0;
606 0           my @dpid = grep { $Proc::Fork::Control::HEAP->{cidlist}->{$_} == 0 } keys %{ $Proc::Fork::Control::HEAP->{cidlist} };
  0            
  0            
607 0 0         if(@dpid){
608 0           for(@dpid){
609 0 0         if(exists $Proc::Fork::Control::HEAP->{cidlist}->{$_}){
610 0           delete $Proc::Fork::Control::HEAP->{cidlist}->{$_};
611 0           $i++;
612              
613 0 0         $Proc::Fork::Control::HEAP->{children} -- if $Proc::Fork::Control::HEAP->{children};
614             }
615             }
616             }
617              
618             # Do some additional checks to see if these children are really alive.
619 0           for(keys %{ $Proc::Fork::Control::HEAP->{cidlist} }){
  0            
620 0 0         if(!kill(0, $_)){
621 0 0         if(exists $Proc::Fork::Control::HEAP->{cidlist}->{$_}){
622 0           delete $Proc::Fork::Control::HEAP->{cidlist}->{$_};
623 0           $i++;
624              
625 0 0         $Proc::Fork::Control::HEAP->{children} -- if $Proc::Fork::Control::HEAP->{children};
626             }
627             }
628             }
629              
630 0           return $i;
631             }
632              
633             1;