perl-modperl mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Torsten Foertsch <torsten.foert...@gmx.net>
Subject Re: Forking to an interactive program under mod_perl
Date Wed, 13 Dec 2006 10:44:08 GMT
On Wednesday 13 December 2006 02:03, Alex Beamish wrote:
> Interesting suggestions, thank you, and I'm coming around to the idea that
> a daemon will need to be used for the heavy lifting .. and then perhaps
> mod_perl can communicate with the daemon using named pipes ..
>
> The whole point of this exercise is to keep all of the page information in
> memory so that when Ghostscript gets another page request it can service it
> right away, rather having to reload everything all over again. Lower
> latency, at the cost of higher memory requirements. So that tells me I
> don't want to load Ghostscript each time a new request comes in.
>
> I'll deal with multiple documents with some combination of stale timers and
> LRU slots, but that's not really what I see as the most complicated or
> difficult part of this problem. For this particular application, my
> inactivity timer will probably by 10-15 minutes, and I'll expect to have
> 6-8 documents open at any given time, so it shouldn't be a big drain on
> memory. And I will probably be able to set something up that signals that a
> document has been expired as well .. (this is just me thinking out loud) ..
>
> Thanks for your feedback .. I think named pipes is my next focus.

Alex,

one thing you might run into while trying to control gs is buffering. Most 
programs buffer their output if STDOUT is not bound to a terminal. That means 
even if you have switched off output buffering in Perl ($|=1) you write a 
command to the external program but won't get an answer until enough output 
is generated to fill up a buffer.

This problem can be solved using pseudo terminals. IO::Pty is handy at this.

Further, I'd use a unix domain socket to talk to your gs-controller-daemon. 

apache + mod_perl
       ^
       |
       | IO::Socket::UNIX
       |
       v
 gs-controller
       ^
       |
       | IO::Pty
       |
       v
       gs

Let gs-controller listen on a socket. Each actual connection to a gs is 
assigned a unique ID by the gs-controller. This ID you can put into your page 
as a session handle. So a subsequent request for the same gs-connection reads 
this ID and transfers it to the gs-controller together with the gs-commands.
gs-controller then finds the actual PTY and submits the commands. Of course, 
your gs-controller has to handle timeouts and errors.

If you need to spawn off the gs-controller daemon on demand from mod_perl 
remember that

1) Apache opens a lot more file descriptors than STDIN, STDOUT and STDERR
2) use CORE::exit if you really want your program to exit (after a fork maybe)
3) Apache signals it's process group if it wants to shut down or restart

Maybe some of the problems can be avoided using Apache2::SubProcess. I have 
never used it. For me the following code does the job:

  sub sysclose {
    require 'syscall.ph';
    syscall &SYS_close, shift()+0;
  }

  sub RLIMIT_NOFILE {7;}                # linux specific
  sub getrlimit {
    require 'syscall.ph';
    my $x="x"x8;			# result
    syscall &SYS_getrlimit, shift()+0, $x;
    unpack "ii", $x;
  }

  sub close_fd {
    my @l=getrlimit( RLIMIT_NOFILE );
    my $l;

    # close all files save STDIN, STDOUT and STDERR
    for( $l=3; $l<$l[0]; $l++ ) {
      sysclose $l;
    }
    sysclose 0;
    open STDIN, '/dev/null';
    sysclose 1;
    open STDOUT, '>/dev/null';
  }

  sub spawn {
    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 ) {
	CORE::exit 0;
      } else {
	close_fd;
	POSIX::setsid if( $daemon_should_survive_apache_restart );
	exec @args;
	CORE::exit 0;
      }
    }
    waitpid $pid, 0;		# avoid a zombie
  }

The spawned process has only 1 open file, STDERR. For mod_perl processes it 
points to the ErrorLog. For processes spawned by CGI scripts (Apache 2.x) it
is a pipe to the Apache child. In this latter case you must open your own 
STDERR.

Torsten

Mime
View raw message