talexb has asked for the wisdom of the Perl Monks concerning the following question:

Further to my post from yesterday, I'm finding out that what I'm trying to do may not be possible.

In short, I am trying to call a potentially long running process (Ghostscript) that is wrapped in some code that sets an alarm, then does an eval to call Ghostscript. Either Ghostscript finishes, or the alarm triggers to catch a runaway page being rendered.

My apache says:

[alex@foo bar]$ sudo /usr/sbin/httpd -V Server version: Apache/2.0.40 Server built: Nov 27 2003 11:04:06 Server's Module Magic Number: xxxxxxxx:x Architecture: 32-bit Server compiled with.... -D APACHE_MPM_DIR="server/mpm/prefork" -D APR_HAS_SENDFILE -D APR_HAS_MMAP -D APR_HAVE_IPV6 -D APR_USE_SYSVSEM_SERIALIZE -D APR_USE_PTHREAD_SERIALIZE -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT -D APR_HAS_OTHER_CHILD -D AP_HAVE_RELIABLE_PIPED_LOGS -D HTTPD_ROOT="/etc/httpd" -D SUEXEC_BIN="/usr/sbin/suexec" -D DEFAULT_PIDLOG="logs/httpd.pid" -D DEFAULT_SCOREBOARD="logs/apache_runtime_status" -D DEFAULT_LOCKFILE="logs/accept.lock" -D DEFAULT_ERRORLOG="logs/error_log" -D AP_TYPES_CONFIG_FILE="conf/mime.types" -D SERVER_CONFIG_FILE="conf/httpd.conf"

I'm not sure if there's anything else on my system that I should be checking.

I've done testing using sudo strace /usr/sbin/httpd -f /home/foo/bar/conf/httpd.conf -X and things seem to go bad as soon as the alarm is set ..

... getgroups32(0x20, 0xbfffeab0) = 1 rt_sigaction(SIGALRM, {0x4044fc80, [], SA_RESTORER, 0x402b88f8}, {SIG_ +DFL}, 8) = 0 time(NULL) = 1164821271 alarm(602) = 0 pipe([9, 10]) = 0 ioctl(9, SNDCTL_TMR_TIMEBASE, 0xbfffe970) = -1 EINVAL (Invalid argumen +t) _llseek(9, 0, 0xbfffe9c0, SEEK_CUR) = -1 ESPIPE (Illegal seek) ioctl(10, SNDCTL_TMR_TIMEBASE, 0xbfffe970) = -1 EINVAL (Invalid argume +nt) _llseek(10, 0, 0xbfffe9c0, SEEK_CUR) = -1 ESPIPE (Illegal seek) fcntl64(9, F_SETFD, FD_CLOEXEC) = 0 fcntl64(10, F_SETFD, FD_CLOEXEC) = 0 pipe([11, 12]) = 0 ioctl(11, SNDCTL_TMR_TIMEBASE, 0xbfffe970) = -1 EINVAL (Invalid argume +nt) _llseek(11, 0, 0xbfffe9c0, SEEK_CUR) = -1 ESPIPE (Illegal seek) ioctl(12, SNDCTL_TMR_TIMEBASE, 0xbfffe970) = -1 EINVAL (Invalid argume +nt) _llseek(12, 0, 0xbfffe9c0, SEEK_CUR) = -1 ESPIPE (Illegal seek) fcntl64(11, F_SETFD, FD_CLOEXEC) = 0 fcntl64(12, F_SETFD, FD_CLOEXEC) = 0 ...

Naturally, I googled for this problem and came up with this post which points to this post which actually contains some really interesting discussion. The post in that thread that seems to sum things up is this one, but this isn't 100% decided. This post later in the thread says

So it may be that I can't use alarms to catch runaway code after all.

Has anyone here had experience using alarms under Apache2?

Alex / talexb / Toronto

"Groklaw is the open-source mentality applied to legal research" ~ Linus Torvalds

Replies are listed 'Best First'.
Re: mod_perl, prefork mpm and alarms
by rhesa (Vicar) on Nov 29, 2006 at 19:12 UTC
    I don't have experience with alarm(), and given that "Use them at your own risk" quote, I don't think I'm going to investigate it either. What I do have experience with, is processing long-running jobs. The general approach I use is to have the web interface kick off a batch job, which runs separate from Apache.

    I happen to use a POE daemon, and track batch jobs through a table in MySQL. The web interface polls this table at regular intervals, and displays the progress (or the end-result) to the user. The POE daemon can process many jobs concurrently (through POE::Component::JobQueue and POE::Wheel::Run).

    The advantage of that approach is that both sides are decoupled, and that you have detailed control over the resource consumption.

    Update: Maybe you could make use of Apache2::SubProcess. That looks like a safe way to fork under mod_perl.

Re: mod_perl, prefork mpm and alarms
by jbert (Priest) on Nov 29, 2006 at 19:03 UTC
    No, but as a possible workaround, if you are displaying a page relying on the output of ghostscript then you could resort to good old fashioned polling, just make sure you avoid busy-waiting.

    I don't know if you can call sleep (since that might rely on an underlying SIGALRM) - it's worth trying to see if you can) and if not you can (ab)use good old select undef, undef, undef, x.y; to sleep.

    You could then have your child process create a file with the desired output when it is done (note that you don't simply want to check for the existence of the output file - you could end up with a partially-written file. Have the child process create into a temp file and then rename (which is atomic) to the output file).

    Since you are periodically waking up (say every 1 sec) you can implement your timeout via a counter. On timeout, you can nuke the child process, print an apologetic page, remove the temp file and get on with life.

    Not very nice, I know, but waking once a second isn't a burdensome operation and if it's a probably-long-running task, you probably don't need a greater resolution than that.

Re: mod_perl, prefork mpm and alarms
by shmem (Chancellor) on Nov 29, 2006 at 19:09 UTC
    I'm not that firm with the guts of mod_perl - but could you fork instead?
    if ((my $pid == fork()) == 0) { exec 'ghostscript', @args; } else { my $slept; while($slept++ < $alarmtime) { sleep 1; last unless kill 0, $pid; # see if it's still alive } kill 0, $pid or kill 15, $pid; wait; }

    That way you wouldn't need signal handlers. Oh wait... SIGCHLD and the wait()...

    --shmem

    update: added loop

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: mod_perl, prefork mpm and alarms
by perrin (Chancellor) on Nov 29, 2006 at 19:15 UTC
    So, what's the problem? I didn't see it in your post. The main issue I'm aware of with alarm is that recent versions of perl (5.6+ I think) use "safe signal handling" which means you may not get the signal for a little bit. You can turn it off. There is some discussion of this problem in DBIx-Timeout.

      The problem is that the request goes to call Ghostscript and never comes back, possibly because of some incompatibility with alarm.

      I've since commented out all of the calls to alarm, and the problem remains.

      Alex / talexb / Toronto

      "Groklaw is the open-source mentality applied to legal research" ~ Linus Torvalds

        So it's not alarm then. If you call ghostscript the same way from a command-line perl program, does it also hang?
Re: mod_perl, prefork mpm and alarms
by gam3 (Curate) on Nov 30, 2006 at 04:15 UTC
    You might want too look into BSD::Resourse as talked about in 528103. This not exactly what you are asking for, but might solve your problem.
    -- gam3
    A picture is worth a thousand words, but takes 200K.