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