From de499029f9041eb8dc617cd6bab4ddd108bc0f9f Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Mon, 21 Jul 2008 15:32:25 +0000
Subject: [PATCH] Add signal handler class

This signal handler class abstracts some of the code previously
used in other places. It also uninstalls its handler when Reset()
is called or the class is destructed, thereby restoring the
previous behaviour.

Reviewed-by: iustinp
---
 lib/utils.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/lib/utils.py b/lib/utils.py
index ceacc9b5e..7548a7e15 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -40,6 +40,7 @@ import select
 import fcntl
 import resource
 import logging
+import signal
 
 from cStringIO import StringIO
 
@@ -1094,3 +1095,71 @@ def LockFile(fd):
     if err.errno == errno.EAGAIN:
       raise errors.LockError("File already locked")
     raise
+
+
+class SignalHandler(object):
+  """Generic signal handler class.
+
+  It automatically restores the original handler when deconstructed or when
+  Reset() is called. You can either pass your own handler function in or query
+  the "called" attribute to detect whether the signal was sent.
+
+  """
+  def __init__(self, signum):
+    """Constructs a new SignalHandler instance.
+
+    @param signum: Single signal number or set of signal numbers
+
+    """
+    if isinstance(signum, (int, long)):
+      self.signum = set([signum])
+    else:
+      self.signum = set(signum)
+
+    self.called = False
+
+    self._previous = {}
+    try:
+      for signum in self.signum:
+        # Setup handler
+        prev_handler = signal.signal(signum, self._HandleSignal)
+        try:
+          self._previous[signum] = prev_handler
+        except:
+          # Restore previous handler
+          signal.signal(signum, prev_handler)
+          raise
+    except:
+      # Reset all handlers
+      self.Reset()
+      # Here we have a race condition: a handler may have already been called,
+      # but there's not much we can do about it at this point.
+      raise
+
+  def __del__(self):
+    self.Reset()
+
+  def Reset(self):
+    """Restore previous handler.
+
+    """
+    for signum, prev_handler in self._previous.items():
+      signal.signal(signum, prev_handler)
+      # If successful, remove from dict
+      del self._previous[signum]
+
+  def Clear(self):
+    """Unsets "called" flag.
+
+    This function can be used in case a signal may arrive several times.
+
+    """
+    self.called = False
+
+  def _HandleSignal(self, signum, frame):
+    """Actual signal handling function.
+
+    """
+    # This is not nice and not absolutely atomic, but it appears to be the only
+    # solution in Python -- there are no atomic types.
+    self.called = True
-- 
GitLab