HWRF  trunk@4391
Classes | Functions | Variables
produtil.sigsafety Namespace Reference

Sets up signal handlers to ensure a clean exit. More...

Detailed Description

Sets up signal handlers to ensure a clean exit.

This module is a workaround for a deficiency of Python. When Python receives a fatal signal other than SIGINT, it exits immediately without freeing utilized resources or otherwise cleaning up. This module causes Python to raise a fatal exception, that does NOT derive from Exception, if a fatal signal is received. Note that there is a critical flaw in this design: raising an exception in a signal handler only raises it in the main (initial) thread. Other threads must call the produtil.sigsafety.checksig function as frequently as possible to check if a signal has been caught. That function will raise the appropriate exception if a signal was caught, or return immediately otherwise.

The reason this HAD to be added to produtil is that the lack of proper signal handling caused major problems. In particular, it completely broke file locking on Lustre and Panasas. Both filesystems will sometimes forget a file lock is released if the lock was held by a process that exited abnormally. There were also unverified cases of this happening with GPFS. Correctly handling SIGTERM, SIGQUIT, SIGHUP and SIGINT has solved that problem thus far.

The base class of any exception thrown due to a signal is CaughtSignal. It has two subclasses: FatalSignal, which is raised when a fatal signal is received, and HangupSignal. The HangupSignal is raised by SIGHUP, unless the install_handlers requests otherwise. Scripts should catch HangupSignal if the program is intended to ignore hangups. However, nothing should ever catch FatalSignal. Only exit and finalize blocks should be run in that situation, and they should run as quickly as possible.

The install_handlers installs the signal handlers: term_handler and optionally hup_handler. The raise_signals option specifies the list of signals that will raise FatalSignal, defaulting to SIGTERM, SIGINT and SIGQUIT. If SIGHUP is added to that list, then it will raise FatalSignal as well. Otherwise, the ignore_hup option controls the SIGHUP behavior: if True, SIGHUP is simply ignored, otherwise it raises HangupSignal.

One can call install_handlers directly, though it is recommended to call produtil.setup.setup instead.

Classes

class  CaughtSignal
 Base class of the exceptions thrown when a signal is caught. More...
 
class  FatalSignal
 Raised when a fatal signal is caught, as defined by the call to install_handlers. More...
 
class  HangupSignal
 With the default settings to install_handlers, this is raised when a SIGHUP is caught. More...
 

Functions

def checksig ()
 This should be called frequently from worker threads to determine if the main thread has received a signal. More...
 
def uninstall_handlers ()
 Resets all signal handlers to their system-default settings (SIG_DFL). More...
 
def hup_handler (signum, frame)
 This is the signal handler for raising HangupSignal: it is used only for SIGHUP, and only if that is not specified in raise_signals and ignore_hup=False. More...
 
def term_handler (signum, frame)
 This is the signal handler for raising FatalSignal. More...
 
def install_handlers
 Installs signal handlers that will raise exceptions. More...
 

Variables

list defaultsigs = [signal.SIGTERM,signal.SIGINT,signal.SIGQUIT]
 Default signals for which to install terminal handlers. More...
 
tuple modifiedsigs = list()
 List of signals modified by install_handlers.
 
list __all__ = ['CaughtSignal','HangupSignal','FatalSignal','install_handlers','checksig']
 List of symbols exported by "from produtil.sigsafety import *".
 
 caught_signal = None
 The signal number of the signal that was caught or None if no signal has been caught. More...
 
 caught_class = None
 The class that should be raised due to the caught signal, or None if no signal has been caught. More...
 

Function Documentation

def produtil.sigsafety.checksig ( )

This should be called frequently from worker threads to determine if the main thread has received a signal.

If a signal was caught this function will raise the appropriate subclass of CaughtSignal. Otherwise, it returns None.

Definition at line 97 of file sigsafety.py.

Referenced by produtil.workpool.WorkPool.add_work().

def produtil.sigsafety.hup_handler (   signum,
  frame 
)

This is the signal handler for raising HangupSignal: it is used only for SIGHUP, and only if that is not specified in raise_signals and ignore_hup=False.

Parameters
signum,framesignal information

Definition at line 129 of file sigsafety.py.

def produtil.sigsafety.install_handlers (   ignore_hup = False,
  raise_signals = defaultsigs 
)

Installs signal handlers that will raise exceptions.

Parameters
ignore_hupIf True, SIGHUP is ignored, else SIGHUP will raise HangupSignal
raise_signals- List of exceptions that will raise FatalSignal. If SIGHUP is in this list, that overrides any decision made through ignore_hup.

Definition at line 153 of file sigsafety.py.

Referenced by produtil.setup.setup().

def produtil.sigsafety.term_handler (   signum,
  frame 
)

This is the signal handler for raising FatalSignal.

Parameters
signum,framesignal information

Definition at line 141 of file sigsafety.py.

def produtil.sigsafety.uninstall_handlers ( )

Resets all signal handlers to their system-default settings (SIG_DFL).

Does NOT restore the original handlers.

This function is a workaround for a design flaw in Python threading: you cannot kill a thread. This workaround restores default signal handlers after a signal is caught, ensuring the next signal will entirely terminate Python. Only the term_handler calls this function, so repeated hangups will still be ignored if the code desires it.

Some may note you can kill a Python thread on Linux using a private function but it is not available on all platforms and breaks GC. Another common workaround in Python is to use Thread.daemon, but that kills the thread immediately, preventing the thread from killing external processes or cleaning up other resources upon parent exit.

Definition at line 109 of file sigsafety.py.

Referenced by produtil.sigsafety.term_handler().

Variable Documentation

produtil.sigsafety.caught_class = None

The class that should be raised due to the caught signal, or None if no signal has been caught.

This is initialized by the signal handlers, and used by checksig to raise exceptions due to caught signals.

Definition at line 95 of file sigsafety.py.

produtil.sigsafety.caught_signal = None

The signal number of the signal that was caught or None if no signal has been caught.

This is initialized by the signal handlers, and used by checksig to raise exceptions due to caught signals.

Definition at line 88 of file sigsafety.py.

produtil.sigsafety.defaultsigs = [signal.SIGTERM,signal.SIGINT,signal.SIGQUIT]

Default signals for which to install terminal handlers.

Definition at line 48 of file sigsafety.py.