[openlmi-tools/f20] fix signal handling

Peter Hatina phatina at fedoraproject.org
Mon Sep 22 07:09:45 UTC 2014


commit c5237b3957bd7d89cfab7c8743fec218c3f43943
Author: Peter Hatina <phatina at redhat.com>
Date:   Mon Sep 22 08:28:36 2014 +0200

    fix signal handling

 openlmi-tools-02-signal.patch |  229 +++++++++++++++++++++++++++++++++++++++++
 openlmi-tools.spec            |    8 ++-
 2 files changed, 236 insertions(+), 1 deletions(-)
---
diff --git a/openlmi-tools-02-signal.patch b/openlmi-tools-02-signal.patch
new file mode 100644
index 0000000..d202962
--- /dev/null
+++ b/openlmi-tools-02-signal.patch
@@ -0,0 +1,229 @@
+diff --git a/cli/lmi/shell/LMIMethod.py b/cli/lmi/shell/LMIMethod.py
+index dc1e8d1..9ce1f82 100644
+--- a/cli/lmi/shell/LMIMethod.py
++++ b/cli/lmi/shell/LMIMethod.py
+@@ -55,53 +55,95 @@ from LMIExceptions import LMIHandlerNamePatternError
+ 
+ logger = logging.getLogger(__name__)
+ 
+-class LMISignalHelper(object):
++
++class LMISignalHelperBase(object):
+     """
+-    Helper class, which takes care of signal (de)registration and handling.
++    Base signal handling class.
+     """
+ 
+-    _instance = None
+-
+-    def __new__(cls):
+-        if cls._instance is None:
+-            cls._instance = super(LMISignalHelper, cls).__new__(cls)
+-            LMISignalHelper.reset(cls._instance)
+-        return cls._instance
++    @staticmethod
++    def signal(signo, handler):
++        """
++        Calls signal() for signo, handler and returns the old signal handler.
++        If signo is list of signals, the signal() call is applied for each
++        signo. If handler is also list, each signal from signo will be handled
++        by corresponding handler. In such case, tuple of previous handlers will
++        be returned.
++        """
++        if isinstance(signo, (list, tuple)):
++            if not isinstance(handler, (list, tuple)):
++                handler = [handler] * len(signo)
++            signo_handler = zip(signo, handler)
++            if not signo_handler:
++                return (None,) * len(signo)
++            old_handlers = []
++            for signal, handler in signo_handler:
++                old_handlers.append(
++                    LMISignalHelperBase.signal_core(signal, handler))
++            return tuple(old_handlers)
++        else:
++            return LMISignalHelperBase.signal_core(signal, handler)
+ 
+-    def reset(self):
++    @staticmethod
++    def signal_core(signo, handler):
+         """
+-        Resets the single instance into default state.
++        Wrapper method for signal.signal(). In case of ValueError, it returns
++        None, old signal handler otherwise. If handler is None, default signal
++        handler is set for such signal.
+         """
+-        self._handler_sigint  = None
+-        self._handler_sigterm = None
+-        self._signal_handled  = False
+-        self._instance._callbacks = collections.OrderedDict()
++        try:
++            if handler is None:
++                handler = signal.SIG_DFL
++            return signal.signal(signo, handler)
++        except ValueError:
++            return None
++
++
++class LMIMethodSignalHelper(LMISignalHelperBase):
++    """
++    Helper class which takes care of signal (de)registration and handling.
++    """
++
++    INTERRUPT_SIGNALS = (
++        signal.SIGINT,
++        signal.SIGTERM)
++
++    def __init__(self):
++        super(LMIMethodSignalHelper, self).__init__()
++        self._signal_handled = False
++        self._signal_prev_handlers = (signal.SIG_DFL,) * 2
++        self._callbacks = collections.OrderedDict()
+ 
+     def signal_attach(self):
+         """
+-        Registers *SIGINT* and *SIGTERM* signals to local handler in which, the flags for
+-        each signal are modified, if such signal is caught.
++        Registers *SIGINT* and *SIGTERM* signals to local handler in which, the
++        flags for each signal are modified, if such signal is caught.
+         """
+-        self._signal_handled  = False
+-        self._handler_sigint  = signal.signal(signal.SIGINT,  LMISignalHelper.__signal_handler)
+-        self._handler_sigterm = signal.signal(signal.SIGTERM, LMISignalHelper.__signal_handler)
++        def handler(sig, action):
++            self.signal_handler(sig, action)
++
++        self._signal_handled = False
++        self._signal_prev_handlers = self.signal(
++            self.INTERRUPT_SIGNALS, handler)
+ 
+     def signal_detach(self):
+         """
+-        Unregisters *SIGINT* and *SIGTERM* handler and removes all the attached callbacks.
++        Unregisters *SIGINT* and *SIGTERM* handler and removes all the attached
++        callbacks.
+         """
+-        signal.signal(signal.SIGINT,  self._handler_sigint)
+-        signal.signal(signal.SIGTERM, self._handler_sigterm)
++        self.signal(self.INTERRUPT_SIGNALS, self._signal_prev_handlers)
+ 
+     def signal_handled(self):
+         """
+-        :returns: True, if any of *SIGINT* or *SIGTERM* has been caught; False otherwise
++        :returns: True, if any of *SIGINT* or *SIGTERM* has been caught; \
++            False otherwise
+         """
+         return self._signal_handled
+ 
+     def callback_attach(self, cb_name, cb):
+         """
+-        Registers a callback, which will be called when a *SIGINT* or *SIGTERM* is caught.
++        Registers a callback, which will be called when a *SIGINT* or *SIGTERM*
++        is caught.
+ 
+         :param string cb_name: callback name
+         :param cb: callable object, which takes zero arguments
+@@ -116,21 +158,18 @@ class LMISignalHelper(object):
+         """
+         self._callbacks.pop(cb_name)
+ 
+-    @staticmethod
+-    def __signal_handler(signo, frame):
++    def signal_handler(self, signo, frame):
+         """
+-        Signal handler, which is called, when *SIGINT* and *SIGTERM* are sent to
+-        the LMIShell.
++        Signal handler, which is called, when *SIGINT* and *SIGTERM* are sent
++        to the LMIShell.
+ 
+         :param int signo: signal number
+         :param frame: -- stack frame
+-
+-        **NOTE:** see help(signal)
+         """
+-        if signo in (signal.SIGINT, signal.SIGTERM):
+-            LMISignalHelper._instance._signal_handled = True
+-        for cb in LMISignalHelper._instance._callbacks.values():
+-            cb()
++        if signo in self.INTERRUPT_SIGNALS:
++            self._signal_handled = True
++        [cb() for cb in self._callbacks.values()]
++
+ 
+ class LMIMethod(LMIWrapperBaseObject):
+     """
+@@ -350,13 +389,15 @@ class LMIMethod(LMIWrapperBaseObject):
+ 
+         # Register signal callback for SIGINT, SIGTERM with callback,
+         # which awakes waiting thread for immediate return.
+-        LMISignalHelper().callback_attach("indication", lambda: LMIMethod.__wake(cond))
+-        LMISignalHelper().signal_attach()
++        signal_helper = LMIMethodSignalHelper()
++        signal_helper.callback_attach(
++            "indication", lambda: LMIMethod.__wake(cond))
++        signal_helper.signal_attach()
+ 
+         # Wait for the job to finish
+         wake_cnt = 0
+         cond.acquire()
+-        while not LMISignalHelper().signal_handled() and \
++        while not signal_helper.signal_handled() and \
+                 not job_finished.value and \
+                 not lmi_is_job_finished(job_inst):
+             cond.wait(LMIMethod._COND_WAIT_TIME)
+@@ -382,8 +423,8 @@ class LMIMethod(LMIWrapperBaseObject):
+                     break
+ 
+         # Unregister signal handler
+-        LMISignalHelper().signal_detach()
+-        LMISignalHelper().callback_detach("indication")
++        signal_helper.signal_detach()
++        signal_helper.callback_detach("indication")
+ 
+         cond.release()
+ 
+@@ -393,7 +434,7 @@ class LMIMethod(LMIWrapperBaseObject):
+         self._conn._client._delete_instance(cim_handler.path)
+         if job_exception.value:
+             raise job_exception.value
+-        if LMISignalHelper().signal_handled() and not job_finished.value:
++        if signal_helper.signal_handled() and not job_finished.value:
+             # We got SIGINT or SIGTERM, when waiting for the job, cancelling the job
+             logger.warn("Cancelling a job '%s'" % job_inst.Name)
+             job_inst.RequestStateChange(
+@@ -418,8 +459,10 @@ class LMIMethod(LMIWrapperBaseObject):
+ 
+         # Register signal callback for SIGINT, SIGTERM with callback,
+         # which awakes waiting thread for immediate return.
+-        LMISignalHelper().callback_attach("polling", lambda: LMIMethod.__wake(cond))
+-        LMISignalHelper().signal_attach()
++        signal_helper = LMIMethodSignalHelper()
++        signal_helper.callback_attach(
++            "polling", lambda: LMIMethod.__wake(cond))
++        signal_helper.signal_attach()
+ 
+         cond = threading.Condition()
+         cond.acquire()
+@@ -428,7 +471,7 @@ class LMIMethod(LMIWrapperBaseObject):
+ 
+         try:
+             sleep_time = 1
+-            while not LMISignalHelper().signal_handled() and \
++            while not signal_helper.signal_handled() and \
+                     not lmi_is_job_finished(job_inst):
+                 # Sleep, a bit longer in every iteration
+                 cond.wait(sleep_time)
+@@ -448,10 +491,10 @@ class LMIMethod(LMIWrapperBaseObject):
+             cond.release()
+ 
+         # Unregister signal handler and callback
+-        LMISignalHelper().signal_detach()
+-        LMISignalHelper().callback_detach("polling")
++        signal_helper.signal_detach()
++        signal_helper.callback_detach("polling")
+ 
+-        if LMISignalHelper().signal_handled() and not lmi_is_job_finished(job_inst):
++        if signal_helper.signal_handled() and not lmi_is_job_finished(job_inst):
+             # We got SIGINT or SIGTERM, when waiting for the job, cancelling the job
+             logger.warn("Cancelling a job '%s'" % job_inst.Name)
+             job_inst.RequestStateChange(
diff --git a/openlmi-tools.spec b/openlmi-tools.spec
index 24ac307..6278fbd 100644
--- a/openlmi-tools.spec
+++ b/openlmi-tools.spec
@@ -1,12 +1,13 @@
 Name:           openlmi-tools
 Version:        0.9.2
-Release:        2%{?dist}
+Release:        3%{?dist}
 Summary:        Set of CLI tools for Openlmi providers
 
 License:        GPLv2+
 URL:            http://fedorahosted.org/openlmi
 Source0:        http://fedorahosted.org/released/openlmi-tools/%{name}-%{version}.tar.gz
 Patch0:         openlmi-tools-01-logging.patch
+Patch1:         openlmi-tools-02-signal.patch
 BuildArch:      noarch
 
 BuildRequires:  automake
@@ -44,6 +45,7 @@ Group:          Documentation
 %prep
 %setup -q
 %patch0 -p1 -b .logging
+%patch1 -p1 -b .signal
 
 %build
 make
@@ -116,6 +118,10 @@ install -m 644 cli/completion/_lmishell $zsh_comp_dir
 %{_docdir}/%{name}-%{version}/html
 
 %changelog
+* Mon Sep 22 2014 Peter Hatina <phatina at redhat.com> - 0.9.2-3
+- fix signal handling
+- Resolves: rhbz#1138573
+
 * Wed Jul 16 2014 Peter Hatina <phatina at redhat.com> - 0.9.2-2
 - fix unset logging handlers
 - Resolves: rhbz#1119747


More information about the scm-commits mailing list