[f21/master] Fix a race between checking for Gtk.main_level and running Gtk.main

David Shea dshea at redhat.com
Wed Oct 8 19:05:12 UTC 2014


Ensure that if an exception occurs before the GUI has started, causing
meh to start its own Gtk main loop, that the GUI really doesn't start
another Gtk main loop.
---
 pyanaconda/anaconda.py        | 10 ++++++++--
 pyanaconda/exception.py       | 13 ++++++++-----
 pyanaconda/ui/__init__.py     |  5 ++++-
 pyanaconda/ui/gui/__init__.py | 13 +++++++------
 pyanaconda/ui/tui/__init__.py |  4 ++--
 5 files changed, 29 insertions(+), 16 deletions(-)

diff --git a/pyanaconda/anaconda.py b/pyanaconda/anaconda.py
index 88c2e02..c207640 100644
--- a/pyanaconda/anaconda.py
+++ b/pyanaconda/anaconda.py
@@ -31,6 +31,7 @@ import sys
 import stat
 from glob import glob
 from tempfile import mkstemp
+import threading
 
 from pyanaconda.bootloader import get_bootloader
 from pyanaconda import constants
@@ -78,6 +79,11 @@ class Anaconda(object):
         self.dbus_session_connection = None
         self.dbus_inhibit_id = None
 
+        # This is used to synchronize Gtk.main calls between the graphical
+        # interface and error dialogs. Whoever gets to their initialization code
+        # first will lock gui_initializing
+        self.gui_initialized = threading.Lock()
+
     @property
     def bootloader(self):
         if not self._bootloader:
@@ -206,7 +212,7 @@ class Anaconda(object):
         if self.displayMode == 'g':
             from pyanaconda.ui.gui import GraphicalUserInterface
             self._intf = GraphicalUserInterface(self.storage, self.payload,
-                                                self.instClass)
+                                                self.instClass, self.gui_initialized)
 
             # needs to be refreshed now we know if gui or tui will take place
             addon_paths = addons.collect_addon_paths(constants.ADDON_PATHS,
@@ -214,7 +220,7 @@ class Anaconda(object):
         elif self.displayMode in ['t', 'c']: # text and command line are the same
             from pyanaconda.ui.tui import TextUserInterface
             self._intf = TextUserInterface(self.storage, self.payload,
-                                           self.instClass)
+                                           self.instClass, self.gui_initialized)
 
             # needs to be refreshed now we know if gui or tui will take place
             addon_paths = addons.collect_addon_paths(constants.ADDON_PATHS,
diff --git a/pyanaconda/exception.py b/pyanaconda/exception.py
index 0c5fe91..b60ac0f 100644
--- a/pyanaconda/exception.py
+++ b/pyanaconda/exception.py
@@ -50,7 +50,7 @@ log = logging.getLogger("anaconda")
 
 class AnacondaExceptionHandler(ExceptionHandler):
 
-    def __init__(self, confObj, intfClass, exnClass, tty_num):
+    def __init__(self, confObj, intfClass, exnClass, tty_num, gui_lock):
         """
         :see: python-meh's ExceptionHandler
         :param tty_num: the number of tty the interface is running on
@@ -58,6 +58,7 @@ class AnacondaExceptionHandler(ExceptionHandler):
         """
 
         ExceptionHandler.__init__(self, confObj, intfClass, exnClass)
+        self._gui_lock = gui_lock
         self._intf_tty_num = tty_num
 
     def run_handleException(self, dump_info):
@@ -110,9 +111,10 @@ class AnacondaExceptionHandler(ExceptionHandler):
                 if not initialized:
                     raise RuntimeError()
 
-                if Gtk.main_level() > 0:
-                    # main loop is running, don't crash it by running another one
-                    # potentially from a different thread
+                # Attempt to grab the GUI initializing lock, do not block
+                if not self._gui_lock.acquire(False):
+                    # the graphical interface is running, don't crash it by
+                    # running another one potentially from a different thread
                     log.debug("Gtk running, queuing exception handler to the "
                              "main loop")
                     GLib.idle_add(self.run_handleException, dump_info)
@@ -267,7 +269,8 @@ def initExceptionHandling(anaconda):
         conf.register_callback("journalctl", journalctl_callback, attchmnt_only=False)
 
     handler = AnacondaExceptionHandler(conf, anaconda.intf.meh_interface,
-                                       ReverseExceptionDump, anaconda.intf.tty_num)
+                                       ReverseExceptionDump, anaconda.intf.tty_num,
+                                       anaconda.gui_initialized)
     handler.install(anaconda)
 
     return conf
diff --git a/pyanaconda/ui/__init__.py b/pyanaconda/ui/__init__.py
index b1eb060..28b24bb 100644
--- a/pyanaconda/ui/__init__.py
+++ b/pyanaconda/ui/__init__.py
@@ -42,7 +42,7 @@ class UserInterface(object):
        defines what kinds of dialogs and entry widgets every interface must
        provide that the rest of anaconda may rely upon.
     """
-    def __init__(self, storage, payload, instclass):
+    def __init__(self, storage, payload, instclass, gui_lock):
         """Create a new UserInterface instance.
 
            The arguments this base class accepts defines the API that interfaces
@@ -60,6 +60,8 @@ class UserInterface(object):
                            is useful for determining distribution-specific
                            installation information like default package
                            selections and default partitioning.
+           gui_lock     -- A lock object acquired by the first thread to initialize
+                           a GUI.
         """
         if self.__class__ is UserInterface:
             raise TypeError("UserInterface is an abstract class.")
@@ -67,6 +69,7 @@ class UserInterface(object):
         self.storage = storage
         self.payload = payload
         self.instclass = instclass
+        self.gui_lock = gui_lock
 
         # Register this interface with the top-level ErrorHandler.
         from pyanaconda.errors import errorHandler
diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py
index 3582718..7c3df99 100644
--- a/pyanaconda/ui/gui/__init__.py
+++ b/pyanaconda/ui/gui/__init__.py
@@ -429,11 +429,11 @@ class GraphicalUserInterface(UserInterface):
     """This is the standard GTK+ interface we try to steer everything to using.
        It is suitable for use both directly and via VNC.
     """
-    def __init__(self, storage, payload, instclass,
+    def __init__(self, storage, payload, instclass, gui_lock,
                  distributionText = product.distributionText, isFinal = product.isFinal,
                  quitDialog = QuitDialog):
 
-        UserInterface.__init__(self, storage, payload, instclass)
+        UserInterface.__init__(self, storage, payload, instclass, gui_lock)
 
         self._actions = []
         self._currentAction = None
@@ -563,7 +563,8 @@ class GraphicalUserInterface(UserInterface):
         if not success:
             raise RuntimeError("Failed to initialize Gtk")
 
-        if Gtk.main_level() > 0:
+        # Check if the GUI lock has already been taken
+        if not self.gui_lock.acquire(False):
             # Gtk main loop running. That means python-meh caught exception
             # and runs its main loop. Do not crash Gtk by running another one
             # from a different thread and just wait until python-meh is
@@ -571,10 +572,10 @@ class GraphicalUserInterface(UserInterface):
             unbusyCursor()
             log.error("Unhandled exception caught, waiting for python-meh to "\
                       "exit")
-            while Gtk.main_level() > 0:
-                time.sleep(2)
 
-            sys.exit(0)
+            # Loop forever, meh will call sys.exit() when it's done
+            while True:
+                time.sleep(10000)
 
         while not self._currentAction:
             self._currentAction = self._instantiateAction(self._actions[0])
diff --git a/pyanaconda/ui/tui/__init__.py b/pyanaconda/ui/tui/__init__.py
index 0fb1ca7..731437a 100644
--- a/pyanaconda/ui/tui/__init__.py
+++ b/pyanaconda/ui/tui/__init__.py
@@ -58,7 +58,7 @@ class TextUserInterface(ui.UserInterface):
 
     ENVIRONMENT = "anaconda"
 
-    def __init__(self, storage, payload, instclass,
+    def __init__(self, storage, payload, instclass, gui_lock,
                  productTitle = u"Anaconda", isFinal = True,
                  quitMessage = None):
         """
@@ -91,7 +91,7 @@ class TextUserInterface(ui.UserInterface):
 
         """
 
-        ui.UserInterface.__init__(self, storage, payload, instclass)
+        ui.UserInterface.__init__(self, storage, payload, instclass, gui_lock)
         self._app = None
         self._meh_interface = meh.ui.text.TextIntf()
 
-- 
2.1.0



More information about the anaconda-patches mailing list