From: Greg Ercolano <erco@(email surpressed)>
Subject: Re: How to detect and handle Frame MaxTime failures
   Date: Thu, 03 Nov 2011 20:54:18 -0400
Msg# 2147
View Complete Thread (5 articles) | All Threads
Last Next
On 11/03/11 15:58, Greg Ercolano wrote:
> 	I'd suggest instead of using MaxTime, to handle this
> 	specific set of circumstances, you'd probably want to
> 	instead put some logic in your script to handle the
> 	more specific behavior you want.
> 
> 	For instance, I could see having your own "Render Max Time:"
> 	field in the submit form that passes the value to the
> 	render script, which in turn would take this value,
> 	fork()s the render off as a child, and then monitors
> 	the execution time of the render.
> 
> 	This way the script can decide if it should kill the render,
> 	and if so, implement its own logic to modify the job.

    As an actual perl coding example, here's a unix-specific technique
    that defines a function called 'RunCommandMaxTime()' that takes
    two arguments: the command to run, and the max # seconds.

    So calling it is as simple as:

my $cmd     = "yourcommand -arg1 -arg2 ..";  # COMMAND TO RUN
my $maxsecs = 800;                           # HOW MANY SECONDS IS 'TOO LONG'..
RunCommandMaxTime($cmd, $maxsecs);

    What follows is the definition of that function, which you can customize
    to include whatever post-kill logic you want (see '# ADD POST KILL LOGIC HERE').

    You could add this function to the .common.pl file, so that any of the
    submit scripts could use it if you wanted.

--- snip
use POSIX;

# RUN A COMMAND WITH A MAXIMUM TIME
#    Unix only.
#    $1 -- command to run
#    $2 -- maximum number of seconds command should take before being killed
#
sub RunCommandMaxTime($$)
{
    my ($cmd, $maxtime) = @_;
    my $starttime = time();
    my $pid = fork();
    if ( $pid == -1 )
    {
        # ERROR
        print "ERROR: fork() failed?! $!\n";
        exit(1);
    }
    elsif ( $pid == 0 )
    {
        # CHILD PROCESS
        POSIX::setsid();
        exec($cmd);
        print "ERROR: exec() failed: $!\n";
        exit(1);
    }
    else
    {
        # PARENT -- WATCH CHILD
        my $childpid   = $pid;
        my $exitstatus = 0;
        my $killed     = 0;
        while ( 1 )
        {
            # WATCH THE CHILD PROCESS
            #     See if it finished, and if so, reap.
            #     If it didn't, see if maxtime expired. If so, kill and reap.
            #     Otherwise, keep waiting..
            #
            my $kid = POSIX::waitpid($childpid, WNOHANG);       # see if child finished
            if ( $kid > 0 ) { $exitstatus = $?; last; } # finished? reap + break loop
            # SEE IF MAXTIME EXPIRED
            if ( ( time() - $starttime ) > $maxtime )
            {
                print STDERR "\n--- MAXTIME EXPIRED! Killing child..\n";
                kill(-9, $childpid);   # -9 means kill *process group*
                $killed = 1;
                # Add logic here that you want to do if maxtime expired
            }
            sleep(1);
        }

        # CHILD FINISHED
        if ( $killed ) { print STDERR "--- Render took too long and was killed.\n"; exit(1); }
        print STDERR "Child finished in time. EXITCODE=" . ($exitstatus >> 8) . " (status=$exitstatus)\n";
    }
}
--- snip


PS. If you're instead using windows, you'd have to replace the fork()/exec() stuff
    with the WIN32 equivalent, which in activestate perl is possible with
    'use Win32::Process;' and a combo of Win32::Process::Create() to background
    the child, and Wait() with some number of seconds, and GetExitCode().
    There's actually an example of this in .common.pl

    To handle killing the process, I would stay away from any of the win32 stuff,
    and simply call 'rush -fail $ENV{RUSH_FRAME}' to cause the script to
    commit suicide "cleanly", as the logic for getting that right is tricky
    to do from a script.

    If I decide on vacationing in the sixth circle of hell, I can follow up with
    the WIN32 equivalent code.


Last Next