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

View File

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

View File

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

View File

@ -349,7 +349,7 @@ class vmmCreate(gobject.GObject):
# Storage # Storage
if not self.host_storage_timer: 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.host_space_tick)
self.window.get_widget("enable-storage").set_active(True) self.window.get_widget("enable-storage").set_active(True)
self.window.get_widget("config-storage-create").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: if origxml != self._xml:
# 'tick' to make sure we have the latest time # 'tick' to make sure we have the latest time
self.tick(time.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): def _redefine(self, xml_func, *args):
""" """
@ -1848,7 +1848,7 @@ class vmmDomain(vmmDomainBase):
self._startup_vcpus = None self._startup_vcpus = None
self.vcpu_max_count() self.vcpu_max_count()
self.lastStatus = status 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): def tick(self, now):
@ -1917,7 +1917,7 @@ class vmmDomain(vmmDomainBase):
self.record.insert(0, newStats) self.record.insert(0, newStats)
self._update_status(info[0]) 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): class vmmDomainVirtinst(vmmDomainBase):

View File

@ -191,6 +191,8 @@ class vmmEngine(gobject.GObject):
gobject.source_remove(self.timer) gobject.source_remove(self.timer)
self.timer = None 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) self.timer = gobject.timeout_add(interval, self.tick)
def tick(self): def tick(self):
@ -205,7 +207,7 @@ class vmmEngine(gobject.GObject):
self._tick_thread = threading.Thread(name="Tick thread", self._tick_thread = threading.Thread(name="Tick thread",
target=self._tick, args=()) target=self._tick, args=())
self._tick_thread.daemon = False self._tick_thread.daemon = True
self._tick_thread.start() self._tick_thread.start()
return 1 return 1
@ -221,7 +223,7 @@ class vmmEngine(gobject.GObject):
logging.exception("Could not refresh connection %s." % uri) logging.exception("Could not refresh connection %s." % uri)
logging.debug("Closing connection since libvirtd " logging.debug("Closing connection since libvirtd "
"appears to have stopped.") "appears to have stopped.")
gobject.idle_add(conn.close) util.safe_idle_add(conn.close)
else: else:
raise raise
return 1 return 1

View File

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

View File

@ -18,12 +18,14 @@
# MA 02110-1301 USA. # MA 02110-1301 USA.
# #
import logging
import gtk import gtk
import libxml2 import gobject
import os.path
import libvirt import libvirt
import libxml2
import logging
import os.path
import virtManager import virtManager
import virtinst import virtinst
@ -245,6 +247,25 @@ def idle_emit(self, signal, *args):
self.emit(signal, *args) self.emit(signal, *args)
return False 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): def uuidstr(rawuuid):
hx = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'] hx = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
uuid = [] uuid = []