Make sure all idle and timeout routines are thread safe.

Unbeknownst to me, these functions are not run thread safe:

http://library.gnome.org/devel/gdk/unstable/gdk-Threads.html

However since they are run from the main loop, the chance of them conflicting
with another running thread is slim, since we have very few threads that
actually update the UI.
This commit is contained in:
Cole Robinson 2010-02-11 12:32:00 -05:00
parent 494a93cccc
commit dfb8f8666a
8 changed files with 55 additions and 37 deletions

View File

@ -25,6 +25,8 @@ import gtk.gdk
import gtk.glade
import gobject
from virtManager import util
# Displays a progress bar while executing the "callback" method.
class vmmAsyncJob(gobject.GObject):
@ -61,7 +63,7 @@ class vmmAsyncJob(gobject.GObject):
self.is_pulsing = True
def run(self):
timer = gobject.timeout_add (100, self.exit_if_necessary)
timer = util.safe_timeout_add(100, self.exit_if_necessary)
self.topwin.present()
self.topwin.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
self.bg_thread.start()
@ -74,7 +76,7 @@ class vmmAsyncJob(gobject.GObject):
# async dialog is running. This forces us to clean up properly
# and not leave a dead process around.
logging.debug("Forcing main_quit from async job.")
self._exit_if_necessary(force_exit=True)
self.exit_if_necessary(force_exit=True)
self.topwin.destroy()
@ -132,16 +134,10 @@ class vmmAsyncJob(gobject.GObject):
return (None, None)
return self._error_info
def exit_if_necessary(self):
gtk.gdk.threads_enter()
try:
return self._exit_if_necessary()
finally:
gtk.gdk.threads_leave()
def _exit_if_necessary(self, force_exit=False):
def exit_if_necessary(self, force_exit=False):
if self.bg_thread.isAlive() and not force_exit:
if (self.is_pulsing):
# Don't call pulse_pbar: this function is thread wrapped
self.pbar.pulse()
return True
else:

View File

@ -966,14 +966,14 @@ class vmmConnection(gobject.GObject):
# We want to kill off this thread asap, so schedule a gobject
# idle even to inform the UI of result
logging.debug("Background open thread complete, scheduling notify")
gobject.idle_add(self._open_notify)
util.safe_idle_add(self._open_notify)
self.connectThread = None
def _open_notify(self):
logging.debug("Notifying open result")
try:
gobject.idle_add(util.idle_emit, self, "state-changed")
util.safe_idle_add(util.idle_emit, self, "state-changed")
if self.state == self.STATE_ACTIVE:
caps = self.get_capabilities_xml()
@ -988,7 +988,7 @@ class vmmConnection(gobject.GObject):
self.vms.keys())
if self.state == self.STATE_DISCONNECTED:
gobject.idle_add(util.idle_emit, self, "connect-error",
util.safe_idle_add(util.idle_emit, self, "connect-error",
self.connectError)
self.connectError = None
finally:
@ -1445,7 +1445,7 @@ class vmmConnection(gobject.GObject):
for name in newNodedevs:
self.emit("nodedev-added", self.uri, name)
gobject.idle_add(tick_send_signals)
util.safe_idle_add(tick_send_signals)
# Finally, we sample each domain
now = time()
@ -1468,7 +1468,7 @@ class vmmConnection(gobject.GObject):
if not noStatsUpdate:
self._recalculate_stats(now)
gobject.idle_add(util.idle_emit, self, "resources-sampled")
util.safe_idle_add(util.idle_emit, self, "resources-sampled")
return 1

View File

@ -33,6 +33,7 @@ import socket
import logging
import traceback
from virtManager import util
from virtManager.error import vmmErrorDialog
# Console pages
@ -477,7 +478,7 @@ class vmmConsolePages(gobject.GObject):
logging.error("Too many connection failures, not retrying again")
return
logging.warn("Retrying connection in %d ms", self.vncViewerRetryDelay)
gobject.timeout_add(self.vncViewerRetryDelay, self.retry_login)
util.safe_timeout_add(self.vncViewerRetryDelay, self.retry_login)
if self.vncViewerRetryDelay < 2000:
self.vncViewerRetryDelay = self.vncViewerRetryDelay * 2
@ -489,12 +490,8 @@ class vmmConsolePages(gobject.GObject):
libvirt.VIR_DOMAIN_CRASHED ]:
return
gtk.gdk.threads_enter()
try:
self.try_login()
return
finally:
gtk.gdk.threads_leave()
def open_tunnel(self, server, vncaddr, vncport, username, sshport):
if self.vncTunnel is not None:
@ -676,7 +673,7 @@ class vmmConsolePages(gobject.GObject):
def unset_cb(src):
src.queue_resize_no_redraw()
gobject.idle_add(restore_scroll, src)
util.safe_idle_add(restore_scroll, src)
return False
def request_cb(src, req):
@ -686,7 +683,7 @@ class vmmConsolePages(gobject.GObject):
src.disconnect(signal_id)
gobject.idle_add(unset_cb, widget)
util.safe_idle_add(unset_cb, widget)
return False
# Disable scroll bars while we resize, since resizing to the VM's

View File

@ -349,7 +349,7 @@ class vmmCreate(gobject.GObject):
# Storage
if not self.host_storage_timer:
self.host_storage_timer = gobject.timeout_add(3 * 1000,
self.host_storage_timer = util.safe_timeout_add(3 * 1000,
self.host_space_tick)
self.window.get_widget("enable-storage").set_active(True)
self.window.get_widget("config-storage-create").set_active(True)

View File

@ -1357,7 +1357,7 @@ class vmmDomain(vmmDomainBase):
if origxml != self._xml:
# 'tick' to make sure we have the latest time
self.tick(time.time())
gobject.idle_add(util.idle_emit, self, "config-changed")
util.safe_idle_add(util.idle_emit, self, "config-changed")
def _redefine(self, xml_func, *args):
"""
@ -1848,7 +1848,7 @@ class vmmDomain(vmmDomainBase):
self._startup_vcpus = None
self.vcpu_max_count()
self.lastStatus = status
gobject.idle_add(util.idle_emit, self, "status-changed", status)
util.safe_idle_add(util.idle_emit, self, "status-changed", status)
def tick(self, now):
@ -1917,7 +1917,7 @@ class vmmDomain(vmmDomainBase):
self.record.insert(0, newStats)
self._update_status(info[0])
gobject.idle_add(util.idle_emit, self, "resources-sampled")
util.safe_idle_add(util.idle_emit, self, "resources-sampled")
class vmmDomainVirtinst(vmmDomainBase):

View File

@ -191,6 +191,8 @@ class vmmEngine(gobject.GObject):
gobject.source_remove(self.timer)
self.timer = None
# No need to use 'safe_timeout_add', the tick should be
# manually made thread safe
self.timer = gobject.timeout_add(interval, self.tick)
def tick(self):
@ -205,7 +207,7 @@ class vmmEngine(gobject.GObject):
self._tick_thread = threading.Thread(name="Tick thread",
target=self._tick, args=())
self._tick_thread.daemon = False
self._tick_thread.daemon = True
self._tick_thread.start()
return 1
@ -221,7 +223,7 @@ class vmmEngine(gobject.GObject):
logging.exception("Could not refresh connection %s." % uri)
logging.debug("Closing connection since libvirtd "
"appears to have stopped.")
gobject.idle_add(conn.close)
util.safe_idle_add(conn.close)
else:
raise
return 1

View File

@ -23,6 +23,8 @@ import logging
import virtinst
from virtManager import util
MEDIA_FLOPPY = "floppy"
MEDIA_CDROM = "cdrom"
@ -136,7 +138,7 @@ class vmmMediaDevice(gobject.GObject):
if self.poll_signal:
return
self.poll_signal = gobject.timeout_add(MEDIA_TIMEOUT * 1000,
self.poll_signal = util.safe_timeout_add(MEDIA_TIMEOUT * 1000,
self._poll_for_media)
def disable_poll_for_media(self):

View File

@ -18,12 +18,14 @@
# MA 02110-1301 USA.
#
import logging
import gtk
import libxml2
import os.path
import gobject
import libvirt
import libxml2
import logging
import os.path
import virtManager
import virtinst
@ -245,6 +247,25 @@ def idle_emit(self, signal, *args):
self.emit(signal, *args)
return False
def _safe_wrapper(func, *args):
gtk.gdk.threads_enter()
try:
func(*args)
finally:
gtk.gdk.threads_leave()
def safe_idle_add(func, *args):
"""
Make sure idle functions are run thread safe
"""
return gobject.idle_add(_safe_wrapper, func, *args)
def safe_timeout_add(timeout, func, *args):
"""
Make sure timeout functions are run thread safe
"""
return gobject.timeout_add(timeout, _safe_wrapper, func, *args)
def uuidstr(rawuuid):
hx = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
uuid = []