Acme-Spork

 view release on metacpan or  search on metacpan

lib/Acme/Spork.pm  view on Meta::CPAN

our @EXPORT_OK = qw(daemonize daemonize_without_close_on);

use version;our $VERSION = qv('0.0.8');

our %reopen_stdfhs_to;

sub import {
    shift->export_to_level(1, grep(!/^-/, @_));
    if(grep /^-reopen_stdfhs$/, @_) {
        %reopen_stdfhs_to = (
             STDIN  => [qw(< /dev/null)],
             STDOUT => [qw(> /dev/null)],
             STDERR => [qw(>&STDOUT)],
        );
    }
    return;
}

sub spork {
    my $spork = shift;
    croak "spork() needs a code ref!" if ref $spork ne 'CODE';
    
    my $PARENT_WTR = IO::Handle->new;
    my $CHILD_RDR  = IO::Handle->new;

    pipe($CHILD_RDR,  $PARENT_WTR); # or return/croak ?
    $PARENT_WTR->autoflush(1);

    defined (my $kid = fork) or die "Cannot fork: $!\n";
    
    if ($kid) {
        close $PARENT_WTR;
        chomp(my $grandkid_pid = <$CHILD_RDR>);
        close $CHILD_RDR; 
        waitpid($kid,0);
        return $grandkid_pid;
    }
    else {
        ## local $SIG{CHLD} = 'IGNORE';
        if (!defined &setsid) {                
            require POSIX;
            *setsid = *POSIX::setsid;
        }
        setsid();
        ##
        
        defined ( my $grandkid = fork) or die "Kid cannot fork: $!\n";
        if ($grandkid) {
            close $CHILD_RDR; 
            print $PARENT_WTR "$grandkid\n";
            close $PARENT_WTR;
            CORE::exit(0);
        }
        else {
            close $CHILD_RDR;
            close $PARENT_WTR;
            
            for my $stdfh (qw(STDIN STDOUT STDERR)) {
                close $stdfh;
                if(exists $reopen_stdfhs_to{ $stdfh } && ref $reopen_stdfhs_to{ $stdfh } eq 'ARRAY') {
                    eval  "open( $stdfh, " . join(', ', map { qq{"$_"} } @{ $reopen_stdfhs_to{ $stdfh } }) . ' );';
                    carp "Could not reopen $stdfh : $@" if $@; 
                    # no strict 'refs';
                    # open( $stdfh , @{ $reopen_stdfhs_to{ $stdfh } }) or carp "Could not reopen $stdfh : $!";
                }
            }
            
            ## if (!defined &setsid) {                
            ##     require POSIX;
            ##     *setsid = *POSIX::setsid;
            ## }
            ## 
            ## setsid();
            ## $SIG{CHLD} = 'DEFAULT';
            $spork->(@_);
            CORE::exit(0);
        }
    }
}   

sub daemonize {
    require Proc::Daemon;
    {
        local $SIG{'HUP'} = $SIG{'HUP'} || ''; # workaround until http://rt.cpan.org/Public/Bug/Display.html?id=21453
        goto &Proc::Daemon::Init;
    }
}

sub daemonize_without_close_on {
    require Proc::Daemon;
    {
        no warnings 'redefine';
        local *POSIX::close = sub { return 1; }; # the "without_close_on" part

        local $SIG{'HUP'} = $SIG{'HUP'} || ''; # workaround until http://rt.cpan.org/Public/Bug/Display.html?id=21453
        Proc::Daemon::Init(@_);
    }
}

1;

__END__

=head1 NAME

Acme::Spork - Perl extension for spork()ing in your script

=head1 SYNOPSIS

    use Acme::Spork;
    my $spork_pid = spork(\&long_running_code, @ARGV) 
        or die "Could not fork for spork: $!";
    print "Long running code has been started as PID $spork_pid, bye!\n";

=head1 DESCRIPTION

A spork in the plastic sense is a fork combined with a spoon. In programming I've come to call a spork() a fork() that does more than just a fork.

I use it to describe when you want to fork() to run some long running code but immediately return to the main program instead of waiting for it.

=head1 spork()



( run in 1.045 second using v1.01-cache-2.11-cpan-140bd7fdf52 )