[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