U g @sdZddlZddlZddlZejejejgZe a dddddgZ Gddde Z Gd dde ZGd dde Zdadad dZd d ZddZddZdefddZdS)a!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.N CaughtSignal HangupSignal FatalSignalinstall_handlerschecksigc@s eZdZdZddZddZdS)ra!Base class of the exceptions thrown when a signal is caught. Note that this does not derive from Exception, to ensure it is not caught accidentally. At present, it derives directly from KeyboardInterrupt, though that may be changed in the future to BaseException.cCst|||_dS)zS!CaughtSignal constructor @param signum the signal that was caught (an int)N) BaseException__init__signum)selfr r ?/lfs/h1/ops/prod/packages/hafs.v2.0.7/ush/produtil/sigsafety.pyrBs zCaughtSignal.__init__cCs d|jfS)z%! A string description of this error.zCaught signal %d)r )r r r r __str__JszCaughtSignal.__str__N)__name__ __module__ __qualname____doc__rr r r r r r<sc@seZdZdZdS)rz!With the default settings to install_handlers, this is raised when a SIGHUP is caught. Note that this does not derive from Exception.Nrrrrr r r r rMsc@seZdZdZdS)rz!Raised when a fatal signal is caught, as defined by the call to install_handlers. Note that this does not derive from Exception.Nrr r r r rQscCs$t}t}|dk r |dk r ||dS)z!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.N) caught_signal caught_class)csZccr r r rcs cCstD]}t|tjqdS)aI!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.N) modifiedsigssignalSIG_DFL)Zisigr r r uninstall_handlersosrcCs|atatjt|dS)z!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. @param signum,frame signal informationN)rrrprodutillockingdisable_lockingr framer r r hup_handlers rcCs.|atatjtjtt|dS)z_!This is the signal handler for raising FatalSignal. @param signum,frame signal informationN) rrrrrrZpipelineZkill_allrrr r r term_handlers   r FcCsP|rttjtjntj|kr.ttjt|D]}t|tq2t|adS)aQ!Installs signal handlers that will raise exceptions. @param ignore_hup If True, SIGHUP is ignored, else SIGHUP will raise HangupSignal @param raise_signals - List of exceptions that will raise FatalSignal. If SIGHUP is in this list, that overrides any decision made through ignore_hup. N)rSIGHUPSIG_IGNrr listr) ignore_hupZ raise_signalsZsigr r r rs  )rZprodutil.lockingrZprodutil.pipelinerSIGTERMSIGINTSIGQUITZ defaultsigsr#r__all__KeyboardInterruptrrrrrrrrr rr r r r s*