mirror of
https://github.com/virt-manager/virt-manager.git
synced 2024-12-22 13:34:07 +03:00
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:
parent
494a93cccc
commit
dfb8f8666a
@ -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):
|
||||
if (self.is_pulsing):
|
||||
# Don't call pulse_pbar: this function is thread wrapped
|
||||
self.pbar.pulse()
|
||||
return True
|
||||
else:
|
||||
|
@ -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,8 +988,8 @@ class vmmConnection(gobject.GObject):
|
||||
self.vms.keys())
|
||||
|
||||
if self.state == self.STATE_DISCONNECTED:
|
||||
gobject.idle_add(util.idle_emit, self, "connect-error",
|
||||
self.connectError)
|
||||
util.safe_idle_add(util.idle_emit, self, "connect-error",
|
||||
self.connectError)
|
||||
self.connectError = None
|
||||
finally:
|
||||
self.connectThreadEvent.set()
|
||||
@ -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
|
||||
|
||||
|
@ -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()
|
||||
self.try_login()
|
||||
return
|
||||
|
||||
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
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -23,6 +23,8 @@ import logging
|
||||
|
||||
import virtinst
|
||||
|
||||
from virtManager import util
|
||||
|
||||
MEDIA_FLOPPY = "floppy"
|
||||
MEDIA_CDROM = "cdrom"
|
||||
|
||||
@ -136,8 +138,8 @@ class vmmMediaDevice(gobject.GObject):
|
||||
if self.poll_signal:
|
||||
return
|
||||
|
||||
self.poll_signal = gobject.timeout_add(MEDIA_TIMEOUT * 1000,
|
||||
self._poll_for_media)
|
||||
self.poll_signal = util.safe_timeout_add(MEDIA_TIMEOUT * 1000,
|
||||
self._poll_for_media)
|
||||
|
||||
def disable_poll_for_media(self):
|
||||
self.poll_signal = None
|
||||
|
@ -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 = []
|
||||
|
Loading…
Reference in New Issue
Block a user