http://qs321.pair.com?node_id=11111522

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

I have an Autosys triggered Perl script that does a load of stuff and then sleeps for 15 minutes before doing it all again. I need to find a way to cleanly exit the program but I can't get it to respond to any signal handlers other than KILL and ALRM - both cause an immediate termination. If I add an ALRM signal handler, the ALRM stops working i.e. program keeps running!!!! Any thoughts? Jerry

Replies are listed 'Best First'.
Re: Signal to a sleeping Perl program
by davido (Cardinal) on Jan 17, 2020 at 16:04 UTC

    Does this need to remain resident and sleeping, or could it just be a cron that kicks off at 15 minute intervals? On Windows you could hand it to the task scheduler.

    This still has the issue of how to stop it running; it's inconvenient to always be manipulating crontabs or task scheduler entities. But if the script started out like this:

    __PACKAGE__->run(@ARGV) if -e '/path/to/run-me.flag' && ! caller; sub run { # ... the work goes here... }

    ...you would prevent future runs from doing anything. This doesn't address an stopping an in-progress run, but I think you are probably trying to prevent the script from waking up again. With this approach you just touch a file into existence if you want the script to proceed when the cron picks it up again. If the file has been removed the script won't proceed. You wouldn't want to leave an impotent cron laying around forever but if it's just a matter of needing to prevent it running for a few hours or days once in awhile, this seems like a reasonable solution.


    Dave

Re: Signal to a sleeping Perl program
by choroba (Cardinal) on Jan 17, 2020 at 14:33 UTC
    What OS is it running in? I gladly forgot most about Autosys after quitting $job - 1.

    The following runs in Linux and reacts to signals as expected:

    #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; local $SIG{USR1} = sub { say STDERR "Signal 1 caught."; alarm 0; }; local $SIG{USR2} = sub { say STDERR "Bye!"; exit }; my $sum = 0; while (1) { $sum += $_ for 1 .. 100; say $sum; say STDERR scalar localtime, " Sleeping..."; sleep 10; say STDERR scalar localtime, " Ready!"; }

    A sample session:

    ▏~ $ 11111522.pl &
    [1] 28265
    5050
    Fri Jan 17 15:28:04 2020 Sleeping...
    Fri Jan 17 15:28:14 2020 Ready!
    10100
    Fri Jan 17 15:28:14 2020 Sleeping...
    ▏~ $ kill -USR1 28265
    Signal 1 caught.
    Fri Jan 17 15:28:17 2020 Ready!
    15150
    Fri Jan 17 15:28:17 2020 Sleeping...
    ▏~ $ kill -USR2 28265
    Bye!
    [1]+  Done                    11111522.pl
    ▏~ $

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      > I gladly forgot most about Autosys after quitting $job - 1

      Thank G*d you didn't write $job-- ! OO

      ... you wouldn't like the implications ... ;-)

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: Signal to a sleeping Perl program
by afoken (Chancellor) on Jan 17, 2020 at 15:36 UTC
    I have [a] [...] script that [...] sleeps [...] If I add an ALRM signal handler, the ALRM stops working

    One problem is in the man page sleep(3), at least on Linux:

    Bugs

    sleep() may be implemented using SIGALRM; mixing calls to alarm(2) and sleep() is a bad idea.

    Using longjmp(3) from a signal handler or modifying the handling of SIGALRM while sleeping will cause undefined results.


    program keeps running!!!! Any thoughts?

    Multiple exclamation marks ...

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: Signal to a sleeping Perl program
by LanX (Saint) on Jan 17, 2020 at 15:14 UTC
Re: Signal to a sleeping Perl program
by soonix (Canon) on Jan 17, 2020 at 19:39 UTC
    I had the opposite idea of davido's: if you loop with a sleep, you could extend the loop condition like this:
    while (! -e '/predefined/location/time_is_up') { do {'your thing'}; sleep 900; }
    or, in appropriate places
    exit if -e '/predefined/location/time_is_up';
    This would cause the script to end if you create that file.
Re: Signal to a sleeping Perl program
by talexb (Chancellor) on Jan 17, 2020 at 14:48 UTC

    One way would be to break up the 15 minute sleep into smaller mini-sleeps. Presumably, waking up would allow your script to respond to the request to terminate.

    Alex / talexb / Toronto

    Thanks PJ. We owe you so much. Groklaw -- RIP -- 2003 to 2013.

      One way would be to break up the 15 minute sleep into smaller mini-sleeps. Presumably, waking up would allow your script to respond to the request to terminate.

      Any signal, or at least any non-ignored signal interrupts sleep. No need for polling. See also sleep:

      Causes the script to sleep for (integer) EXPR seconds, or forever if no argument is given. Returns the integer number of seconds actually slept.

      May be interrupted if the process receives a signal [...]

      Note: sleep() returns how long it actually slept, so your program can continue sleeping for the remaining time if some signal happened in between.

      The C API sleep() behaves similar, see sleep(3), but it returns the remaining sleep time, not the time actually slept.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        Any signal, or at least any non-ignored signal interrupts sleep.

        Many forget this, and expect that a literal sleep 60 may not actually take 60s, but could be anything less than that. In one-off scripts I've seen code like this (untested):

        sub my_sleep { my $sleep_time = (shift or 1); # total time to sleep my $sleep_interval = (shift or 10); # wake up at least this often my $start = time; $stop_time = $start + $sleep_time; while (time < $stop_time) { sleep $sleep_time; # handle wake up tasks } my $elapsed = time - $start; return $elapsed; # total time actually slept (plus shipping and ha +ndling) }

        You may want additional conditions, and use this as a timeout mechanism. There's probably a nice module for that, or roll your own.

        But the point is sleep comes with an unspoken if (...), if something else doesn't wake me up.

        -QM
        --
        Quantum Mechanics: The dreams stuff is made of

Re: Signal to a sleeping Perl program
by ikegami (Patriarch) on Jan 20, 2020 at 15:56 UTC

    Handled signals causes blocking system calls to be interrupted (so the signal can be handled). You need to handle those errors.

    sub uninterruptible_sleep { my $sleep_until = time() + $_[0]; while (1) { my $time_left = $sleep_until - time(); return if $time_left <= 0; sleep($time_left); } }
Re: Signal to a sleeping Perl program
by jerryhone (Sexton) on Jan 19, 2020 at 23:38 UTC
    Thanks everyone for responses. The OS I'm actually working on is Solaris, and I've had various thoughts about how to forcefully break into the loop, but the consensus here seems to be that the signal handler should work - it just doesn't!

      Try using select instead of sleep - at least on linux it does not rely on ALRM, it's a single system call.

      #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11111522 use warnings; # borrowed from choroba use feature qw{ say }; local $SIG{USR1} = sub { say STDERR "Signal 1 caught."; }; local $SIG{USR2} = sub { say STDERR "Bye!"; exit }; my $sum = 0; while (1) { $sum += $_ for 1 .. 100; say $sum; say STDERR scalar localtime, " Sleeping..."; select undef, undef, undef, 10; say STDERR scalar localtime, " Ready!"; }

      I don't have a Solaris to test on, but this works as expected on my linux system.

      Maybe you should have replied earlier, most of us thought you are on Windows.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: Signal to a sleeping Perl program
by Anonymous Monk on Jan 20, 2020 at 15:43 UTC
    I've got the signal handler operational, but I'm still a little puzzled by what was going on...only discovered by dropping deep into the debugger! What I thought was the process hitting the sleep was actually the process disappearing into an Oracle DBI call and not coming out as there was a format conversion error in the SQL . DBI didn't return an error - it just hung and was uninterruptible, and I'm now guessing that the Oracle DBD module was resetting the signal handlers so my signals were ignored. Either way, correcting the SQL has resulted in a process that not only runs and produces required outcome but stops when asked!
      So it's solved! Thanks for the info!

      The thread is long, this info might get overlooked.

      It would have been nice tho if the OP used a SSCCE from the start and responded quicker.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      UPDATED

      redacted snarky remark.