Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Signals and END block

by Marcello (Hermit)
on Aug 04, 2004 at 07:53 UTC ( [id://379922]=perlquestion: print w/replies, xml ) Need Help??

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

Hi,

My Perl server runs 24/7. Whenever the program is stopped, it needs to delete the database record it created on startup so everything is in sync. This is done in the END block. This line will make sure the END block is executed whenever such a signal is received (I know, I could also 'IGNORE' them):

$SIG{TERM} = $SIG{INT} = $SIG{QUIT} = $SIG{HUP} = sub { die; };
Questions:
  • What signal is sent to a process when the machine is restarted (KILL?)
  • Does the KILL signal cause the END block to be executed (since it cannot be caught)?
  • Is it 100% sure the END block is executed using the above code or might there be other situations in which this fails?

    I'm trying to prevent my server from exiting without execution of the END block.

    Thanks for any input

    Update: I'm on Redhat Linux
  • Replies are listed 'Best First'.
    Re: Signals and END block
    by trantor (Chaplain) on Aug 04, 2004 at 08:55 UTC

      When the machine is rebooted or shut down, processes will receive SIGTERM first, having a chance to terminate cleanly, after five seconds the remaining processes will be forcibly terminated with a SIGKILL. You can check for yourself reading the /etc/init.d/halt script in a Red Hat Linux installation.

      As you say, SIGKILL cannot be caught so it won't trigger the END block. However, it is bad practice to abruptly terminate a process with SIGKILL without trying SIGTERM first.

      Finally, as long as the signals you're handling can be caught and make your script die, the END block should always be executed. Why not having the cleanup code in the signal handler itself, though?

      As a side note, why don't you try to detect and delete the stale database record at startup if possible? This seems safer and it prevents loopholes as well, for example if the process is terminated with SIGKILL.

        The halt script indeeds clarifies this question. First a TERM signal and after 5 seconds a KILL signal.

      • Why not having the cleanup code in the signal handler itself, though?

        It also needs to execute when the programs ends normally, so the END block is probably the best place.

      • Why don't you try to detect and delete the stale database record at startup if possible?

        Since I store the PID value of the process in a database, this PID might be re-used by the operating system when deleting the record at startup.
        Thanks for your helpful input.
          Since I store the PID value of the process in a database, this PID might be re-used by the operating system when deleting the record at startup.

          There is no way to handle 100% of the termination cases at the time of termination no matter what you do (e.g. your machine suddenly loses power and the UPS fails), so you must have code that detects stale records at some future point in time. There are several ways of doing this. The canonical way to verify a pid is to record it in a lock file that the process keeps locked while it is running. When you verify the pid, if you can lock the file, the process has obviously died. Another more generic way could be to write a separate "checkpoint" record that you remove in your END block. If you start up and the checkpoint record exists, you remove the stale PID record the that checkpoint record points to as well as the checkpoint record.

          Since I store the PID value of the process in a database, this PID might be re-used by the operating system when deleting the record at startup.
          Right, but you can check to see if that pid exists, and if so, what process actually holds that pid. If the process that holds the pid is not the same as the process you are tracking, then you can assume that your process was killed abruptly and that you can delete the pid-record and start.
          ------------ :Wq Not an editor command: Wq
    Re: Signals and END block
    by Gilimanjaro (Hermit) on Aug 04, 2004 at 11:14 UTC
      I understand what you're trying to do, but I wouldn't use signals for this sort of thing...

      You want the database-record to reflect the 'alive'-status of your perl-script right? But there's just no 'proper' way of doing this; if your scripts gets KILL-9'd, it doesn't get to execute *any* more code...

      One way to sort-of be sure of sync-state, would be to have your perl-script update a timestamp in it's record every so-often (by using SIG_ALARM probably).

      Another script then keeps an eye on all records that haven't had their timestamp updated for the same interval plus a safety margin of your choosing, and performs a clean-up for records that seem 'stale'.

      Your original script could maybe die with a warning if it's update failed, because it would probably mean either a database connectivity issue, or it was incorrectly identified as gone but it willing to comply...

    Re: Signals and END block
    by Anonymous Monk on Aug 04, 2004 at 09:40 UTC
      What signal is sent to a process when the machine is restarted (KILL?)

      On a Linux box a SIGTERM is sent. Any processes that don't terminate on the SIGTERM are then sent a SIGKILL

      Does the KILL signal cause the END block to be executed (since it cannot be caught)?

      I don't think it will execute the END block on a SIGKILL

      Is it 100% sure the END block is executed using the above code or might there be other situations in which this fails?

      If your Perl interpreter exits abnormally, e.g. you hit a Perl bug, it will exit without executing the end block. Of course this is very rare if you are running a standard release of Perl.

    Re: Signals and END block
    by nothingmuch (Priest) on Aug 04, 2004 at 15:54 UTC
      Everyone explained very well what happens with signals and END blocks. Now that that's cleared up...

      If the record file is not accessed or seen by any other process, you can unlink it after it's been openned, and the file handle will stay valid. The kernel will clean up the disk space it uses when the last file descriptor open on it is closed, which will happen whether you close it explicitly, die a clean death, or get shot down by some violent signal.

      -nuffin
      zz zZ Z Z #!perl
    Re: Signals and END block
    by DrHyde (Prior) on Aug 05, 2004 at 09:24 UTC
      Rather than having your program clean up after itself, how about have the database clean up after your program. Transactions are your friend. If you don't COMMIT your data, then when your connection to the db goes away, the changes should be thrown away.
        I love this solution, i would have never thought of it myself, this is why perl monks is so fantastic, Cheers!
        I should really do something about this apathy ... but i just cant be bothered
        Good solution, but only if you use the same database connection to query the table. I have other processes scanning the database table, and then this record never appears until it is COMMIT'ed.

        Cheers
    Re: Signals and END block
    by Anonymous Monk on Aug 04, 2004 at 08:56 UTC
      What signal is sent to a process when the machine is restarted (KILL?)

      Whatever your operating system sends (consult your os manual)

    Re: Signals and END block
    by Anonymous Monk on Aug 06, 2004 at 19:19 UTC
      Your question is really ´How can I know a process is running and get information about it?´. For this question a database is a poor solution. A still poorer solution is a PID-File. The best solution would be a FIFO, aka named pipe. Because every process closes its filedescriptors at the end of its run (even if SIGKILLED) and a FIFO behaves different if its open by another process, you can surely detect if a process is running. Steps to take:
    • Exclusively gain access to the FIFO(done with another FIFO)
    • Stat FIFO: if empty no process
    • Open FIFO
    • Read FIFO
    • Write back to FIFO
    • Close FIFO
    • Give up exclusive access
    • The Server does only:
    • Exclusively gain access to the FIFO(done with another FIFO)
    • Open FIFO
    • Write PID to FIFO
    • Not close FIFO
    • Give up exclusive access

    Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Domain Nodelet?
    Node Status?
    node history
    Node Type: perlquestion [id://379922]
    Approved by Corion
    Front-paged by bart
    help
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this?Last hourOther CB clients
    Other Users?
    Others chilling in the Monastery: (6)
    As of 2024-03-28 11:50 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      No recent polls found