[rhel7-branch][PATCH] Allow catching exceptions from threads (#1063705)

Vratislav Podzimek vpodzime at redhat.com
Tue Feb 11 14:57:51 UTC 2014


By running exception handler every time an exception appears in a thread we
block us from using our features provided by the wait method. Some exceptions
may be non-critical and the waiting thread may recover from the erroneous state.
However, we need to give it a chance to do so.

The wait_all method called before we start the actual installation should make
sure there are no abandoned problematic threads that raised exception nobody
cared about.

(ported and squashed
07b7034ce91a0ac41f08c9c38b26a5c6179571a0
edc5255f3c5fa0d66f56c175b46e47415a2dda16 and
955a61aeb934e2d4196876bc0570e2139feb48b6 from master)

Signed-off-by: Vratislav Podzimek <vpodzime at redhat.com>
---
 pyanaconda/threads.py | 29 ++++++++++++++++++++++++-----
 1 file changed, 24 insertions(+), 5 deletions(-)

diff --git a/pyanaconda/threads.py b/pyanaconda/threads.py
index 854ed56..aebd7f8 100644
--- a/pyanaconda/threads.py
+++ b/pyanaconda/threads.py
@@ -57,9 +57,10 @@ class ThreadManager(object):
                 raise KeyError("Cannot add thread '%s', a thread with the same name already running" % obj.name)
 
             self._objs[obj.name] = obj
-            self._errors[obj.name] = None
             obj.start()
 
+        return obj.name
+
     def remove(self, name):
         """Removes a thread from the list of known objects.  This should only
            be called when a thread exits, or there will be no way to get a
@@ -117,6 +118,12 @@ class ThreadManager(object):
             log.debug("Waiting for thread %s to exit" % name)
             self.wait(name)
 
+        if self.any_errors:
+            thread_names = ", ".join(thread_name for thread_name in self._errors.iterkeys()
+                                     if self._errors[thread_name])
+            msg = "Unhandled errors from the following threads detected: %s" % thread_names
+            raise RuntimeError(msg)
+
     def set_error(self, name, *exc_info):
         """Set the error data for a thread
 
@@ -136,10 +143,15 @@ class ThreadManager(object):
 
     def raise_if_error(self, name):
         """If a thread has failed due to an exception, raise it into the main
-           thread.
+           thread and remove it from errors.
         """
-        if self._errors.get(name):
-            raise self._errors[name][0], self._errors[name][1], self._errors[name][2]
+        if name not in self._errors:
+            # no errors found for the thread
+            return
+
+        exc_info = self._errors.pop(name)
+        if exc_info:
+            raise exc_info
 
     def in_main_thread(self):
         """Return True if it is run in the main thread."""
@@ -180,7 +192,13 @@ class AnacondaThread(threading.Thread):
            when the main process is killed.
     """
     def __init__(self, *args, **kwargs):
+        if "fatal" in kwargs:
+            self._fatal = kwargs.pop("fatal")
+        else:
+            self._fatal = True
+
         threading.Thread.__init__(self, *args, **kwargs)
+
         self.daemon = True
 
     def run(self, *args, **kwargs):
@@ -192,7 +210,8 @@ class AnacondaThread(threading.Thread):
             threading.Thread.run(self, *args, **kwargs)
         except:
             threadMgr.set_error(self.name, *sys.exc_info())
-            sys.excepthook(*sys.exc_info())
+            if self._fatal:
+                sys.excepthook(*sys.exc_info())
         finally:
             threadMgr.remove(self.name)
             log.info("Thread Done: %s (%s)" % (self.name, self.ident))
-- 
1.8.5.3



More information about the anaconda-patches mailing list