Tosh,

On Tuesday 09 February 2010 14:21:57 Tosh Cooey wrote:
> Hi after much trial and all error I am seeing that the browser 
> connection closing is also stopping my subprocess.
> 

I don't know what you are trying to achieve and I don't use 
Apache2::Subprocess much. What I do to fork off processes instead is this:

  my $close_fd=sub {
    use POSIX ();
    my %save=(2=>1);       # keep STDERR
    undef @sa...@_} if( @_ );
    opendir my $d, "/proc/self/fd" or do {
      warn "Cannot open directory /proc/self/fd: $!\n";
      POSIX::_exit -1;  # try this first to avoid buffer flushing
      CORE::exit -1;
    };

    while (defined(my $fd=readdir $d)) {
      next unless $fd=~/^\d+$/;
      POSIX::close $fd unless exists $save{$fd};
    }
  };

  my $spawn=sub {
    use POSIX ();
    my ($daemon_should_survive_apache_restart, @args)=...@_;

    local $SIG{CHLD}='IGNORE';
    my $pid;
    # yes, even fork can fail
    select undef, undef, undef, .1 while( !defined($pid=fork) );
    unless( $pid ) {            # child
      # 2nd fork to cut parent relationship with a mod_perl apache
      select undef, undef, undef, .1 while( !defined($pid=fork) );
      if( $pid ) {
        POSIX::_exit 0;
        CORE::exit 0;
      } else {
        if( ref($daemon_should_survive_apache_restart) ) {
          $close_fd->($daemon_should_survive_apache_restart->{fd});
          POSIX::setsid if( $daemon_should_survive_apache_restart->{survive} );
        } else {
          $close_fd->();
          POSIX::setsid if( $daemon_should_survive_apache_restart );
        }

        if( 'CODE' eq ref $args[0] ) {
          my $f=shift @args;
          # TODO: restore %ENV and exit() behavior
          eval {$f->(@args)};
          CORE::exit 0;
        } else {
          {exec @args;}         # extra block to suppress a warning
          POSIX::_exit -1;
          CORE::exit -1;
        }
      }
    }
    waitpid $pid, 0;            # avoid a zombie on some OS
  };

And this is how to use it:

  $spawn->({survive=>1}, sub {
    my ($n, $sleep)=...@_;
    for(1..$n) {
      warn "sleeping $sleep\n";
      sleep $sleep;
    }
    warn "Done\n";
  }, 10, 2 );

or

  $spawn->
    (undef,         # don't survive apache stop
     qw/bash -c/,'for (( i=0; i<10; i++ )); do echo "opi" >&2; sleep 2; done')

The first argument to $spawn is an options hash with 2 possible options:

  survive=>BOOLEAN    # should the subprocess survive an apache stop or not
  fd=>\...@list_of_open_file_descriptors_to_preserve

Normally the $spawn will close all files save for STDERR. With the fd option 
one can preserve other files such as database connections or other.

With the survive option true the child process will be the leader of a new 
process group.

NOTES:

- The $close_fd function is Linux-specific. I couldn't think of a really 
portable (at least across UNICES) way to get all open file descriptors of the 
current process.

- The waitpid at the end of $spawn is not necessary under Linux since 
$SIG{CHLD}='IGNORE' already takes care of zombies. But other systems need it.

- Keep in mind that %ENV and exit() work different under mod_perl. If you pass 
a subroutine this behavior remains.

Torsten

Reply via email to